elanous 0.2.22 → 0.2.24
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/NOTICE +7 -0
- package/README.md +32 -22
- package/apps/pwa/out/404/index.html +1 -1
- package/apps/pwa/out/404.html +1 -1
- package/apps/pwa/out/_next/static/{aBi6EyMtLsrl7J8eH4kF7 → Q-7iofoxOEv1WX18S35of}/_buildManifest.js +1 -1
- package/apps/pwa/out/_next/static/chunks/{119fc678.6f591cdb84ca5791.js → 0673cc54.8e2ef1c23e49e190.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{a9f2b54d-40c16bd0efdb04b8.js → 0a09e062-34f747a17d8d8c2e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{f8626309.a4e5d362b1d55e59.js → 0e3acd34.d42b2ef87fdf4234.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1168.a0fb3d9db76c00bd.js +77 -0
- package/apps/pwa/out/_next/static/chunks/{7175-350f4ce2873bc038.js → 1325-dc3f3c21eb181cb4.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1357.a843c41d2cd7ec31.js +34 -0
- package/apps/pwa/out/_next/static/chunks/1430.53f49882b17607c2.js +24 -0
- package/apps/pwa/out/_next/static/chunks/1459-be6509b3419d8a9f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6767.8e6c5f6e744fcd9f.js → 1505.5826671109111e75.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/153.0a4e3cd516930491.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1572.7bc291352e241a45.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1593-d07ad068c120dfb0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1608.b7c900baa56cab32.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1615-4d8156febaac4b35.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1646.e03f0fbc26bc72f6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7327-2d69fb321ba73389.js → 1669-da843ed73aa301f5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/176.13ba0659e3e96fd4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1990.9c60f495ef33c070.js +36 -0
- package/apps/pwa/out/_next/static/chunks/2042e469.f86158dd5c1e51a1.js +122 -0
- package/apps/pwa/out/_next/static/chunks/2528.0af5c0e1fad88df6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2543-28ec6aeb80a5e1e1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2561.43c99d6ee914ee6a.js +62 -0
- package/apps/pwa/out/_next/static/chunks/2638.b39ab2f674caa1a5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/272-0eed0fb0aab716b6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7899.597af4a9996c907f.js → 2721.bc9a8f36bd85c07a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2778.67e0c1d484189ccf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2841.10ff309f6a3168af.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2886-9639f97d523d752a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2978.57fb87ce21cc5a85.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3664.15f968c39d15bd28.js +149 -0
- package/apps/pwa/out/_next/static/chunks/3786-935eb3fcde90e496.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3813.df4a01ae89c6f63f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3961-aaae04ed1d32622f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3992.f79c70480e8c4077.js +309 -0
- package/apps/pwa/out/_next/static/chunks/40-413e8641de9ed1e5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4054-521f11a503daed64.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4315.e7356f34c7d8ec79.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4489-bf629de6f04f7681.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4540.557733cc88c6a82b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/462.0275b4338c5b6337.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4658-7ed9266098a787e4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4945-0f4046cfc3228ea3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{d70879db.c66c56a95a91efc6.js → 4c5e95ce.83e9b16add6fcd8c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5105-18b4165c5d3a86b0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5205-6992210fe31f45f4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5287-72898b126d722457.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6158.566c890adc06a459.js → 5313.1697309c532b9856.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/{79b78388.663188133449d707.js → 53746282.1be36038d1abd949.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5474.991b90688c3cb321.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5544-871146192d095594.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5578.ac963634373b28c6.js +286 -0
- package/apps/pwa/out/_next/static/chunks/5935.d5e8ca3e096772e8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5967-c0a80f8ffe5d4050.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1268.0840ff91e083fcbd.js → 5974.360f8441eb0ac1ec.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/6006.84e8b418a7a0db97.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6013-1267ba7454d6764b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6038-1e0f7ee1086da4cf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/617.709dd0019fe99796.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6261.fb9bf94947094636.js +74 -0
- package/apps/pwa/out/_next/static/chunks/6411.1966866fae31ffa9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6481-b829d99aa8d158f5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6541.2b7daa043100d0fa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6571-926ddf024a6ad5df.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6597.2d7a6d77ab485b27.js +64 -0
- package/apps/pwa/out/_next/static/chunks/67.5dd2dc543c2fa3de.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9832.2ab2e818013574c7.js → 6702.ebbd165d5e4a4a14.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/6777-312496b0d774719b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/681.0116f7036cfb4e8d.js +32 -0
- package/apps/pwa/out/_next/static/chunks/6871.30b2bf23b3126e36.js +24 -0
- package/apps/pwa/out/_next/static/chunks/6882.737d366d54e29d83.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7035.54c2c535164b1c9b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7086-8bfa7e8f796a03d9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7126.5e5599f251726b67.js +93 -0
- package/apps/pwa/out/_next/static/chunks/7203-47ff0740a293e284.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7268.017f49dafb5f0ff3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7355.5d65a87e9567ae08.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7392.4018c2a62177f296.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6110.1297d4093fdfbb0d.js → 740.90b31555290236a9.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/7471.3a3d1be50559cfb8.js +148 -0
- package/apps/pwa/out/_next/static/chunks/7500.73489a345cef2201.js +226 -0
- package/apps/pwa/out/_next/static/chunks/{9070.067c106528ec1ae6.js → 7518.49161e8f5814abdb.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7565.c2f72a15c1ed0396.js +27 -0
- package/apps/pwa/out/_next/static/chunks/7651.6d1c984d3d8587a4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7713.7ab863ed59b2d661.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7793.d7cd2026767ddd54.js +115 -0
- package/apps/pwa/out/_next/static/chunks/7837.c7c267e5dceadb11.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7891.09266382daf70d1c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7905.b230b9d06cb4ad5d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7990-cbf6a01bcbdcb7e6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8119-819c549e0c197786.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8186-74ea19a3700d04a8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8194.484f51e273d9abd2.js → 8264.00e434fbd02e9311.js} +16 -16
- package/apps/pwa/out/_next/static/chunks/8267-e7b3262b735ae106.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8386.86518e55b75f7cf5.js +8 -0
- package/apps/pwa/out/_next/static/chunks/{088d8e8a.8069543d082bec12.js → 83cfc5c8.349535543c1548b6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8437.ba6eeeb29590042f.js +23 -0
- package/apps/pwa/out/_next/static/chunks/{1948.5f35b39eef222a94.js → 8438.ee7f0df3b3f6fcb4.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8469-1500be57e582296f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8483-7164cecdaf178ca6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4588.ad3364cb7fb97ada.js → 8532.a86e99acf9b761cb.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/8569.b853a8daa8f31d12.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8722-4c940f02c496ad36.js +1 -0
- package/apps/pwa/out/_next/static/chunks/887-41d0dcf5af48c628.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8925-2abfb48af477fb7f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{88729267.9c6c000157c83f2f.js → 89849432.9eaa2f4c9c010965.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/9039.da86361496797522.js +90 -0
- package/apps/pwa/out/_next/static/chunks/905.d5745a25884754d4.js +94 -0
- package/apps/pwa/out/_next/static/chunks/9089-d909f3d4478b0628.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9096.dc5c7a267f83790c.js +41 -0
- package/apps/pwa/out/_next/static/chunks/93.a567257b6bdb7fe4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9320.419acb9665b46597.js +287 -0
- package/apps/pwa/out/_next/static/chunks/9356.b8ceda6634612ff8.js +79 -0
- package/apps/pwa/out/_next/static/chunks/94.e9e1043b35cd3eed.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9400-7498ea90b2e6ee8f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3522-1de1319d028ac89c.js → 9502-32c5dc22e8f120f5.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/9930-58644af9c813d6e1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9932.367cf4725fedd6e2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9935.1628b904cdc8ce0e.js +104 -0
- package/apps/pwa/out/_next/static/chunks/{7630.2b9a68659053ff4e.js → 9976.8f28864317639fe6.js} +16 -16
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-a2aa8709d193ee0d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-3b9fc39362473ae7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-f2b62c19ca68fef8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-d7602d489edf5f37.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-24282bf9f90031a5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-8b7c5ab6b5bc8d64.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-4eb84d454f8c3415.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-b2e494bdad57fcb5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/{page-80ed511e02c81412.js → page-c0ab229673a8b0eb.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-3e2b45f3c36dbac4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-c22aa511b28743ad.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-26345242e4049f55.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-bf88c0f558d8ef54.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-52fec523e5b5d3ad.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-67723dfdcde92b31.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-a2aa8709d193ee0d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/page-22725e2570f1126b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-1891335950d68b70.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-3267af1ebdc73cf5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-c314b75c11234998.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-9f9c85415f61782b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-c1d4276059085587.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-833aab587f26ad4c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-8526c17ae38e6a50.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/page-f998637a8402115c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-b7bb53828b17b335.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-1b1850de0257e46d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-6f23159fedb41f67.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-1d809a87139dd024.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-da9e570234d481fd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-b92caddb5c715f30.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-8ce3a38e210b810a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-57ffeeb6019440fd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-abbb417bf514630b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-2c2b3f29cec1efa4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-9369279f72fc7b51.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-64861f14039499c3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-ddc4c93efd8ab94b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-a2aa8709d193ee0d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-901a1391b81053bc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-68d9bbaac9841a58.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-b5c0c996f329371e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-89881ccbcf728978.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-0e87a803b3d7165e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/page-58d4e5cb30ef4871.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-02ce4c3b5f39e4d2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-e620d1bb8eadcec2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-acf868c64d77f950.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-54fa605fa6dba457.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-bc79f0833533fa84.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-d5c4398eea03d8f3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/b4e12462-5fab4a813a63dadd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/cd39fa05.fac4545f42a3ef70.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{f6b7c9ba.64a895b8b2fedbdc.js → ce383d63.d2ede338788ed18e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{framework-5c169757551134ed.js → framework-b2763b10dc6b3836.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/main-app-27893e93b0ec193d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-f280047194e1daf8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-e90eff2d0b70564c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-70f2d654b3435d34.js +1 -0
- package/apps/pwa/out/_next/static/chunks/webpack-4794f053bd5e4230.js +1 -0
- package/apps/pwa/out/_next/static/css/72becfd42807b50d.css +1 -0
- package/apps/pwa/out/_next/static/css/d0a48dc099ed86cf.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 -23
- 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 +22 -22
- 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 -1
- package/apps/pwa/out/live-run/index.txt +24 -23
- 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/catalog/models/anthropic/claude-haiku-5-5.yaml +32 -0
- package/catalog/models/anthropic/claude-opus-5-5.yaml +5 -0
- package/catalog/models/anthropic/claude-sonnet-5-5.yaml +31 -0
- package/catalog/models/anthropic/claude-sonnet-5.yaml +8 -2
- package/catalog/models/openai/gpt-6-astra.yaml +6 -0
- package/catalog/models/openai/gpt-6-luna.yaml +6 -0
- package/catalog/models/openai/gpt-6-sol.yaml +6 -0
- package/catalog/models/openrouter/deepseek-v4-pro-0813.yaml +39 -0
- package/catalog/models/openrouter/deepseek-v4.1-flash.yaml +38 -0
- package/catalog/models/openrouter/glm-5.3-flash.yaml +35 -0
- package/catalog/providers/openrouter.yaml +2 -0
- package/graphs/implement-loop.yaml +31 -0
- package/graphs/log-horizon/log-horizon-watch.yaml +25 -0
- package/graphs/log-horizon/recipes.yaml +3 -0
- package/graphs/ops/harness-gc.yaml +17 -0
- package/graphs/ops/recipes.yaml +3 -0
- package/graphs/overlays/README.md +1 -1
- package/graphs/overlays/stop-heal-ladder.yaml +105 -0
- package/graphs/release/recipes.yaml +1 -1
- package/graphs/release/release-loop.yaml +6 -2
- package/graphs/release-rehearsal/recipes.yaml +12 -0
- package/graphs/release-rehearsal/release-rehearsal.yaml +17 -0
- package/package.json +4 -1
- package/scripts/add-api-key.sh +1 -1
- package/scripts/bench-report.ts +82 -3
- package/scripts/ci-typecheck-changed.ts +80 -14
- package/scripts/fleet/bedrock-pod-secret.ts +122 -0
- package/scripts/fleet/node-setup-linux.sh +97 -0
- package/scripts/fleet/node-setup.sh +11 -0
- package/scripts/fleet/zai-pod-secret.ts +86 -0
- package/scripts/hq/hq-fence-wrapper.sh +148 -0
- package/scripts/land-global-checks.ts +3 -0
- package/scripts/lib/typecheck-ratchet.ts +51 -0
- package/scripts/measure-log-diet-2.ts +213 -0
- package/scripts/measure-tsc-affected-roots.ts +289 -0
- package/scripts/openai-relay-server.ts +5 -1
- package/scripts/release-loop/docs-node.ts +258 -43
- package/scripts/release-loop/gate-executor.ts +28 -0
- package/scripts/release-loop/gate-method.ts +59 -0
- package/scripts/release-loop/gate-node.ts +335 -33
- package/scripts/release-loop/gate-wall.ts +208 -0
- package/scripts/release-loop/known-issues-node.ts +53 -8
- package/scripts/release-loop/publish-at.ts +14 -0
- package/scripts/release-loop/publish-node.ts +17 -2
- package/scripts/release-loop/resume-release.ts +40 -4
- package/scripts/release-loop/resume.ts +94 -0
- package/scripts/release-loop/shard-plan.ts +3 -11
- package/scripts/release-loop/tui-node-checks.ts +22 -0
- package/scripts/release-loop/unattended-release.ts +29 -7
- package/scripts/rhythm/daily.ts +51 -2
- package/scripts/rhythm/weekly.ts +3 -2
- package/scripts/tsc-baseline-cache.ts +147 -0
- package/scripts/tsc-main-safety-net.ts +189 -0
- package/scripts/usage-rollup.ts +64 -8
- package/src/acp/codex-plugins.ts +48 -6
- package/src/agent/monad-agent-turn.ts +67 -5
- package/src/agent/run-context.ts +20 -0
- package/src/agent/tool-profile.ts +28 -2
- package/src/agent-mission/acp-judge.ts +8 -2
- package/src/agent-mission/capabilities-cli.ts +16 -1
- package/src/agent-mission/driver.ts +19 -4
- package/src/agent-mission/pr-review-watch.ts +6 -2
- package/src/agent-mission/resource-ladder.ts +8 -3
- package/src/agent-mission/review-loop.ts +41 -7
- package/src/agent-substrate/acp-implementer.ts +139 -0
- package/src/agent-substrate/acp-reviewer.ts +17 -8
- package/src/agent-substrate/pr-reviewer.ts +191 -28
- package/src/agent-substrate/review-intent.ts +6 -4
- package/src/agent-substrate/self-review-cli.ts +155 -21
- package/src/agent-substrate/shard-boundary-intent.ts +92 -26
- package/src/ask-user-question/pending-questions.ts +8 -2
- package/src/autopilot/build/llm-conflict-merge.ts +216 -12
- package/src/autopilot/codebase-grounding.ts +44 -0
- package/src/autopilot/llm-control-brain.ts +13 -1
- package/src/autopilot/mission-codebase-gate.ts +8 -77
- package/src/autopilot/mission-critique.ts +18 -5
- package/src/autopilot/mission-feasibility-negotiate.ts +11 -10
- package/src/autopilot/mission-trace.ts +12 -1
- package/src/autopilot/pr-manager.ts +12 -3
- package/src/autopilot/system-lookback-run.ts +16 -10
- package/src/bench/bench.ts +183 -0
- package/src/boot/acp-server.ts +4 -7
- package/src/boot/acp-socket-path.ts +7 -0
- package/src/briefing/brief-items.ts +21 -13
- package/src/budget/fetchers/grok.ts +15 -5
- package/src/budget/llm-cost.ts +37 -5
- package/src/budget/types.ts +8 -2
- package/src/budget/unified-usage.ts +88 -6
- package/src/chat/index.ts +12 -3
- package/src/cli/agent-cli.ts +20 -0
- package/src/cli/bench-cli.ts +34 -0
- package/src/cli/brief-cli.ts +27 -21
- package/src/cli/cli-user-error.ts +9 -2
- package/src/cli/guardian-cli.ts +26 -1
- package/src/cli/image-cli.ts +101 -0
- package/src/cli/log-event-names.ts +7 -0
- package/src/cli/logs-cli.ts +1 -0
- package/src/cli/mcp-login.ts +111 -37
- package/src/cli/plugin-cli.ts +21 -1
- package/src/cli/pod-cli.ts +26 -0
- package/src/cli/pr-cli.ts +90 -5
- package/src/cli/pr-land-overlap-evidence.ts +279 -0
- package/src/cli/release-cli.ts +63 -12
- package/src/cli/rfc-status.ts +52 -2
- package/src/cli/self-update.ts +277 -10
- package/src/cli/skills-cli.ts +12 -3
- package/src/cli/tasks-cli.ts +241 -30
- package/src/cli/usage-cli.ts +91 -8
- package/src/context-bus/context-first.ts +51 -2
- package/src/context-bus/context-now-surfaces.ts +13 -2
- package/src/context-bus/long-term-memory.ts +3 -1
- package/src/context-bus/nightly-condense.ts +3 -2
- package/src/core-turn/run-goal-loop.ts +87 -2
- package/src/dashboard/ad-run-setup-bindings.ts +279 -0
- package/src/dashboard/chat-fold.ts +20 -0
- package/src/dashboard/first-screen-band.ts +151 -1
- package/src/dashboard/index.ts +45 -281
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +5 -3
- package/src/dashboard/stale-install-check.ts +24 -2
- package/src/dashboard/turn-stream-formatter.ts +25 -0
- package/src/debug/log-diet.ts +190 -0
- package/src/debug/log.ts +70 -17
- package/src/display/coordinator.ts +25 -2
- package/src/domains/coo-admin-tool.ts +27 -0
- package/src/domains/cron-match.ts +1 -0
- package/src/domains/outbound-alert.ts +17 -6
- package/src/domains/release-tool.ts +158 -10
- package/src/domains/schedule-registry.ts +10 -7
- package/src/domains/schedule-runner.ts +1 -0
- package/src/elanous-config-dir.ts +2 -2
- package/src/elanous-daemon.ts +3 -3
- package/src/error-report/report.ts +21 -4
- package/src/execution-loop/launch-gate.ts +1 -1
- package/src/expression/i18n/messages.en.ts +5 -0
- package/src/expression/i18n/messages.ja.ts +5 -0
- package/src/expression/i18n/messages.ko.ts +5 -0
- package/src/expression/i18n/messages.zh.ts +5 -0
- package/src/expression/i18n/types.ts +8 -0
- package/src/github/pr-labels.ts +2 -0
- package/src/graph-runner/graph-cli.ts +42 -1
- package/src/graph-runner/runner.ts +218 -8
- package/src/grok/usage.ts +23 -2
- package/src/grounding/ref-index.ts +253 -0
- package/src/harness/dispatch-backpressure.ts +267 -0
- package/src/harness/harness-clean.ts +199 -78
- package/src/harness/harness-cli-command.ts +229 -35
- package/src/harness/harness-gc.ts +331 -0
- package/src/harness/harness-seams.ts +2 -20
- package/src/harness/harness-stop.ts +4 -2
- package/src/harness/harness-substrate-default.ts +214 -8
- package/src/harness/launch-registry.ts +6 -0
- package/src/harness/nested-elanous-policy.ts +4 -2
- package/src/harness/worktree-branch-prefix.ts +9 -0
- package/src/hq/fence-wrapper.ts +7 -0
- package/src/hq/fence.ts +187 -0
- package/src/hq/hq.ts +33 -2
- package/src/hq/lease.ts +13 -0
- package/src/hq/seats.ts +133 -21
- package/src/images/image-ledger.ts +179 -0
- package/src/index.ts +250 -69
- package/src/instance/child-scope.ts +32 -1
- package/src/instance/dev-project.ts +133 -0
- package/src/instance/resolve.ts +24 -0
- package/src/instance/spawn-elanous.ts +45 -0
- package/src/intake-plane/collect-github.ts +19 -1
- package/src/intake-plane/digest.ts +45 -20
- package/src/intake-plane/lens.ts +36 -8
- package/src/intake-plane/ref-prefetch.ts +110 -0
- package/src/intelligence-map/model-alias.ts +6 -0
- package/src/intelligence-map/model-catalog.ts +113 -0
- package/src/knowledge/kgs/pack.ts +5 -2
- package/src/knowledge/kgs/sqlite-store.ts +15 -0
- package/src/knowledge/tools/knowledge-query.ts +20 -2
- package/src/knowledge/write.ts +234 -2
- package/src/llm/bedrock-eventstream.ts +215 -0
- package/src/llm/bedrock-provider.ts +248 -0
- package/src/llm/bedrock.ts +491 -0
- package/src/llm/openrouter-byok-cost.ts +96 -0
- package/src/llm/openrouter-deepseek.ts +151 -0
- package/src/llm/openrouter-upstream-pins.config.ts +25 -0
- package/src/llm/provider-credentials.ts +3 -1
- package/src/llm/zai-coding-plan.ts +700 -0
- package/src/llm.ts +383 -41
- package/src/loops/budget.ts +150 -0
- package/src/loops/checker.ts +148 -9
- package/src/loops/orchestrator/resync-shadow.ts +34 -7
- package/src/loops/orchestrator/seat-share.ts +6 -49
- package/src/loops/orchestrator/traffic.ts +5 -81
- package/src/market/signed-index.ts +42 -0
- package/src/maturity/feature-maturity.ts +2 -2
- package/src/mcp/mcp-oauth.ts +136 -17
- package/src/model-tier/llm-tier-map.ts +52 -4
- package/src/models/prompts.ts +23 -1
- package/src/mss/logging/horizon-watch.ts +168 -0
- package/src/mss/logging/log-store.ts +262 -26
- package/src/native-tool-catalog.ts +1 -1
- package/src/nexus/api/consult-requests.ts +7 -10
- package/src/nexus/api/discord-trigger-bot.ts +6 -3
- package/src/nexus/api/error-reports.ts +30 -8
- package/src/nexus/api/http-server.ts +8 -0
- package/src/nexus/api/loop-resources.ts +42 -0
- package/src/nexus/api/missions.ts +73 -2
- package/src/nexus/api/ops-api.ts +18 -0
- package/src/nexus/api/outbound-report.ts +8 -3
- package/src/nexus/api/telegram-trigger-bot.ts +6 -3
- package/src/nexus/index.ts +17 -75
- package/src/nexus/outbound/format.ts +2 -0
- package/src/nexus/outbound/router.ts +198 -13
- package/src/nexus/tools-kind.ts +78 -0
- package/src/oauth/codex-account-rotation.ts +55 -20
- package/src/oauth/codex-account-store.ts +451 -23
- package/src/oauth/fallback-chain.ts +75 -15
- package/src/onboarding/first-run.ts +37 -18
- package/src/onboarding/web-first.ts +20 -2
- package/src/onboarding.ts +2 -1
- package/src/ops/machine-heartbeat.ts +185 -0
- package/src/ops/overview-cache.ts +175 -0
- package/src/ops/overview.ts +8 -0
- package/src/plugins/adapters/claude-package.ts +4 -12
- package/src/plugins/core/capability-policy.ts +141 -0
- package/src/plugins/core/host.ts +21 -8
- package/src/plugins/core/manifest.ts +19 -2
- package/src/plugins/core/trust-store.ts +15 -0
- package/src/plugins/core/types.ts +12 -0
- package/src/plugins/install/market-fetch.ts +68 -16
- package/src/plugins/install/plugin-install.ts +187 -16
- package/src/plugins/maker/plugin-maker.ts +25 -20
- package/src/plugins/maker/wizard-generate.ts +16 -2
- package/src/policy/llm-policy.ts +14 -1
- package/src/process/pid-liveness.ts +1 -1
- package/src/prompt-cache/openai.ts +13 -6
- package/src/prompt-cache/openrouter.ts +94 -0
- package/src/prompt-cache/types.ts +5 -0
- package/src/prompt-enhance/enhance.ts +69 -5
- package/src/prompt-library/family-prompt-coverage.ts +1 -0
- package/src/prompt-library/universal-preamble.ts +2 -2
- package/src/registry/discovery/openrouter-live-resolve.ts +122 -0
- package/src/registry/loader.ts +6 -1
- package/src/release-loop/checklist.ts +24 -9
- package/src/release-loop/feature-store.ts +3 -0
- package/src/release-loop/gate-shards.ts +48 -4
- package/src/release-loop/landed-to-green.ts +81 -0
- package/src/release-loop/light-rc.ts +11 -1
- package/src/release-loop/merged-pr-checklist.ts +10 -0
- package/src/release-loop/placement.ts +107 -23
- package/src/release-loop/rehearsal.ts +317 -0
- package/src/release-loop/release-schedule.ts +13 -1
- package/src/release-loop/rubric-types.ts +1 -0
- package/src/release-loop/rubric.ts +2 -2
- package/src/requirements/requirement-ledger.ts +536 -0
- package/src/retro/daily.ts +26 -12
- package/src/seat-loop/seat-loop.ts +66 -11
- package/src/seat-loop/traffic-signal.ts +44 -0
- package/src/self-dev/ask-launch-flow.ts +2 -2
- package/src/self-dev/dev-cli.ts +109 -11
- package/src/self-dev/dev-pipeline.ts +11 -8
- package/src/self-dev/draft-residue.ts +475 -0
- package/src/self-dev/draft-sweep.ts +254 -10
- package/src/self-dev/draft-triage-rules.ts +27 -1
- package/src/self-dev/graph-journey-route.ts +6 -0
- package/src/self-dev/launch-preflight-text.ts +549 -0
- package/src/self-dev/launch-preflight.ts +10 -545
- package/src/self-dev/orchestrate-cli.ts +82 -5
- package/src/self-dev/orchestrate-summary-line.ts +13 -0
- package/src/self-dev/orchestrate.ts +49 -141
- package/src/self-dev/review-provider-fallback.ts +115 -19
- package/src/self-dev/run-store.ts +1 -3
- package/src/self-dev/run-supervisor.ts +190 -36
- package/src/self-dev/run-types.ts +191 -0
- package/src/self-dev/shard-aggregate.ts +61 -13
- package/src/self-dev/shard-plan.ts +5 -1
- package/src/self-dev/shard-run.ts +23 -5
- package/src/self-dev/supervisor-verdict-edges.ts +3 -4
- package/src/self-implement/blocked-draft-policy.ts +3 -19
- package/src/self-implement/budget-gate.ts +53 -13
- package/src/self-implement/child-context-composition.ts +139 -0
- package/src/self-implement/child-llm-preference.ts +13 -5
- package/src/self-implement/claude-acp-implement.ts +67 -0
- package/src/self-implement/draft-on-stop-mode.ts +18 -0
- package/src/self-implement/flex-signals.ts +59 -0
- package/src/self-implement/follow-up-goals.ts +114 -38
- package/src/self-implement/frozen-merges.ts +8 -3
- package/src/self-implement/gate-baseline-reap.ts +157 -0
- package/src/self-implement/gate-baseline.ts +78 -16
- package/src/self-implement/gate-cli.ts +12 -2
- package/src/self-implement/gate-remote.ts +17 -6
- package/src/self-implement/goal-author-decompose.ts +10 -2
- package/src/self-implement/goal-author.ts +48 -13
- package/src/self-implement/goal-digest.ts +19 -0
- package/src/self-implement/graph-overlay-condition.ts +32 -0
- package/src/self-implement/graph-overlay-yaml.ts +41 -7
- package/src/self-implement/graph-templates.ts +33 -1
- package/src/self-implement/graph-yaml.ts +56 -0
- package/src/self-implement/headless-elanous-driver.ts +11 -9
- package/src/self-implement/heal-intake.ts +5 -1
- package/src/self-implement/host-regate.ts +120 -29
- package/src/self-implement/l8-merge-queue.ts +67 -5
- package/src/self-implement/merge-train.ts +131 -0
- package/src/self-implement/off-diff-evidence.ts +9 -0
- package/src/self-implement/orchestrator.ts +271 -25
- package/src/self-implement/review-finding-recurrence.ts +28 -0
- package/src/self-implement/rework-policy.ts +2 -1
- package/src/self-implement/round-summary-card.ts +63 -0
- package/src/self-implement/run-ledger.ts +4 -3
- package/src/self-implement/run-step-projection.ts +147 -0
- package/src/self-implement/run-stop.ts +1 -1
- package/src/self-implement/running-runs.ts +75 -5
- package/src/self-implement/seams.ts +83 -4
- package/src/self-implement/self-implement-cli.ts +12 -5
- package/src/self-implement/stop-autoheal.ts +69 -2
- package/src/self-implement/train-gate-remote.ts +158 -0
- package/src/session-runtime/index.ts +10 -1
- package/src/session-runtime/retry-api.ts +11 -1
- package/src/session-runtime/retry-policy.ts +2 -1
- package/src/skills/gift-install.ts +119 -0
- package/src/skills/runner.ts +59 -2
- package/src/skills/skill-feedback.ts +31 -1
- package/src/skills/skill-tier.ts +1 -0
- package/src/smart-wizard/capability-index.ts +231 -0
- package/src/smart-wizard/rung.ts +74 -0
- package/src/steward/launch.ts +22 -10
- package/src/steward/triage-budget.ts +62 -0
- package/src/steward/triage.ts +87 -24
- package/src/storage/file-lock.ts +8 -1
- package/src/task-agent/actions.ts +66 -5
- package/src/task-agent/card-evidence.ts +59 -13
- package/src/task-agent/completion-evidence.ts +5 -0
- package/src/task-agent/cover.ts +101 -3
- package/src/task-agent/judge.ts +20 -3
- package/src/task-agent/land-order-ledger.ts +95 -0
- package/src/task-agent/land-worktree.ts +201 -0
- package/src/task-agent/live-move-history.ts +219 -0
- package/src/task-agent/live-moves.ts +451 -34
- package/src/task-agent/parent-host.ts +117 -8
- package/src/task-agent/rejudge-on-head.ts +352 -0
- package/src/task-agent/review-prior-fix.ts +146 -0
- package/src/task-agent/review-result-capture.ts +218 -0
- package/src/task-agent/run-adopt.ts +136 -0
- package/src/task-agent/shadow.ts +154 -14
- package/src/task-agent/task-hand.ts +186 -14
- package/src/task-orchestrator/surfaces/pod-account-broker.ts +38 -5
- package/src/task-orchestrator/surfaces/pod-bedrock-child.ts +74 -0
- package/src/task-orchestrator/surfaces/pod-child-default.ts +50 -0
- package/src/task-orchestrator/surfaces/pod-child-providers.ts +42 -0
- package/src/task-orchestrator/surfaces/pod-command-job.ts +6 -3
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +94 -7
- package/src/task-orchestrator/surfaces/pod-image-source.ts +212 -0
- package/src/task-orchestrator/surfaces/pod-lease.ts +110 -17
- package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +32 -3
- package/src/task-orchestrator/surfaces/pod-pool.ts +86 -18
- package/src/task-orchestrator/surfaces/pod-zai-child.ts +101 -0
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +463 -50
- package/src/task-orchestrator/surfaces/self-implement.ts +25 -3
- package/src/telegram-service.ts +5 -1
- package/src/telegram.ts +17 -4
- package/src/time/format.ts +13 -4
- package/src/tool-runtime/goal-author-runtime.ts +1 -0
- package/src/tool-runtime/self-cognition-catalog.ts +23 -0
- package/src/tool-runtime/self-cognition-runtimes.ts +2 -24
- package/src/typecheck-ratchet.ts +1 -0
- package/src/user-config.ts +180 -32
- package/src/ux-sim/slash-menu-frames.ts +168 -0
- package/src/version/packed-revision.json +1 -1
- package/src/web-terminal/preview-tap-registry.ts +5 -2
- package/src/workflow-runtime/triggers/registry.ts +59 -0
- package/src/workflow-runtime/triggers/scheduler.ts +1 -0
- package/src/workflow-runtime/triggers/webhook-source.ts +35 -5
- package/apps/pwa/out/_next/static/chunks/1109.78a7a3288c0259d9.js +0 -148
- package/apps/pwa/out/_next/static/chunks/115.1c6690b8604f81c4.js +0 -32
- package/apps/pwa/out/_next/static/chunks/1262.76a3f08e67c5f816.js +0 -309
- package/apps/pwa/out/_next/static/chunks/1307-08b716476f1d50af.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1358d292.5c87f12cad6eaa91.js +0 -122
- package/apps/pwa/out/_next/static/chunks/1438.27a1a7b8f2308004.js +0 -149
- package/apps/pwa/out/_next/static/chunks/1442.02d85cd88f4c1eac.js +0 -79
- package/apps/pwa/out/_next/static/chunks/1501-1aad662d82ba43f7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1584.9e2dd53da6e6d4b1.js +0 -24
- package/apps/pwa/out/_next/static/chunks/1586.b989b0614afb77e6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1609.2ad355062870151f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1612-f6df0fc0eed7157b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1826-5d279da64bbbc2de.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1923.37f1d5fe7ebe3860.js +0 -27
- package/apps/pwa/out/_next/static/chunks/195-fb1d7690bf30bab4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1986-33e13d648a9df765.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1993.3fd074a2a246234e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/211-9c9090c59e2c5907.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2140.d2daf9ee96e295d1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2196-4700653d12a44b40.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2212.0359851457473efc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/224.583e562365a52f74.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2328.0f37465646bafa7d.js +0 -36
- package/apps/pwa/out/_next/static/chunks/2372.bf71372184991937.js +0 -286
- package/apps/pwa/out/_next/static/chunks/2489-d238d3e2eb5b2bf5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2537-6c289376caa88ca1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2549-a8515c86f9e8cb96.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2655.72cc6a97276c5acc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2699-325cc923ba7b8631.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2743-ef6e8c3ced74cce5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2840-5458ad50c49cac6a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2856.f75d5baef7f49b74.js +0 -1
- package/apps/pwa/out/_next/static/chunks/311.e685738f92e6e2ec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3194-807b212fb8eae065.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3324-d1d2979821dfa845.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3344.0de4b0ec5a454b7a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/335.a13f339e97893437.js +0 -94
- package/apps/pwa/out/_next/static/chunks/3394.646348f34b2500be.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3443.6276909b53d72d53.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3495.abd4254d808bf2dc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3558-bbb5ab553a0471d3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3721.ba5b82db0d9a5572.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3857-5985236d9307f676.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3916-3d02dd1fc257dd3c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3919.9ff7dc25afefee33.js +0 -74
- package/apps/pwa/out/_next/static/chunks/3998.bd9a69d9b50e9bd2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4003.ab6db1dc266576c9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4144-fad095b48a1862ee.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4216.e8ac94b9fe6b96ab.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4327.c44dc2b55cb43ee5.js +0 -34
- package/apps/pwa/out/_next/static/chunks/4401.94257d497b6d26bc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4559.17ca2cdbaa8d414f.js +0 -115
- package/apps/pwa/out/_next/static/chunks/4565-3d7e3fdeb43686a8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4706.97e734bd4c9816ca.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4795-f372490b178583fc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4982.6e44352f751b9a1b.js +0 -77
- package/apps/pwa/out/_next/static/chunks/5342.49c5c5a530e62b50.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5360-08a80abdf3a4da99.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5431.66c58ab1327ee0b7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5633-d6852385000738ef.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5717.bcfcb96e701b82e0.js +0 -90
- package/apps/pwa/out/_next/static/chunks/5755-30d102586789bf7f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5957.3f1a4012388dd962.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5977-5b9e8a45a5b0c823.js +0 -1
- package/apps/pwa/out/_next/static/chunks/602.9bf02ac8e9502598.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6020.3a1b347e2d2dfacc.js +0 -8
- package/apps/pwa/out/_next/static/chunks/6375.fce1d174ba6d0324.js +0 -64
- package/apps/pwa/out/_next/static/chunks/6546.92a828aef441271f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/658.957fdb0a8c6c6c8e.js +0 -287
- package/apps/pwa/out/_next/static/chunks/6706.323590aabb8b1293.js +0 -226
- package/apps/pwa/out/_next/static/chunks/6747.b67947f293a4c02d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6868.000b24a3045c5574.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6877-74399f69c7da1433.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6946.bba74e9c96050f30.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7033.a558f1b2f6912a53.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7065.46029c26c53eac50.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7087.6fae577d09249bc1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/722.de8d9801e0f6d414.js +0 -41
- package/apps/pwa/out/_next/static/chunks/726.83532d69452ee24b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7290-f09c928a06c52e2d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7497.57981e9d7a75a52a.js +0 -24
- package/apps/pwa/out/_next/static/chunks/7672.4af122ad892117c7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/771.6ab889a233e7dae7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7797-df5aa0381fdc078f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8060-cc008cc6fedfa818.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8085-d065d0393c1760f5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8096.f7da0776cfde6300.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8401-35e94e9091f5eb8d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8682-7061dd4fa7bd62ed.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8695-846034ce44852c70.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8769.815ca69879f608b4.js +0 -104
- package/apps/pwa/out/_next/static/chunks/8857-aa1cc4ac90af4d40.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8f835b4f.048eeb02299e3e12.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9068.5f2764fccf86c1b2.js +0 -93
- package/apps/pwa/out/_next/static/chunks/9103-60fbef33e66f0a46.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9247-55bcfbb0e8057f59.js +0 -1
- package/apps/pwa/out/_next/static/chunks/935.4ddaa99fc1d1126c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9451.ebfb0dcc9c02250f.js +0 -23
- package/apps/pwa/out/_next/static/chunks/9559.e648369b1d57cf9a.js +0 -62
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-c1a96669158471ec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-88459e0903da2803.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-d85c43f46f58716b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-4e645600579ae952.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-87edbfd19117823b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-7f0c2b7801ac6a54.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-bf1f030a9d1adffd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-cbaf66b1e1804adf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-9f5610caaa06178c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-ec8f3575e339de74.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-86d753e460f1a989.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-4166a7d51cf0a57d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-2a19c5cd3b562580.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-b6a09cde2e07521a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-c1a96669158471ec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-f7eae1518ca0c353.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-456a8573ad811381.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-8462d6a543cbb2d9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-2974cdfb87c0f9c9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-f927ffa25f3c86d1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-abd86cd832aaa011.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-79daf68f6256adaa.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-8c3f168fc3f91939.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-786fb4504d0f2fa9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-75209f14597dcb8f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-bf30affe3a85af7e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-1187a44b7515434b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-3d0910185a66ed54.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-f401ee1dd5a26e25.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-5dfe7604fd842784.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-76cf838050a8d681.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-7ec8e289b4c14df6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-8c8ac17d52be2a79.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-d359ab048d63a9d9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-b8b345c880bf78d1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-f151cc10ad026940.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-9875f103057da433.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-c1a96669158471ec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-9789328af9a9aa62.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-bf495a1e284f4e00.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-a263d94bca4be22a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-3962eab8ec58d16e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-9abcfac07e02a542.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-bee49f927c333abb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-bac2d56dcb02d955.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-937cbcc13176295c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-d09495594f7f480d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-69cb93d1a2e57e0e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-72ec543928d67c3e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-82906eeda0186a7b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/f6231284-45726550aba08fca.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-59fe95fed40c7a01.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-4ca74dbbee7057c6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-13a0a886df4d97a1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-6af8591cd3a3f038.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-f2b4016b9b981bc8.js +0 -1
- package/apps/pwa/out/_next/static/css/f2bd5257e6af88d8.css +0 -1
- /package/apps/pwa/out/_next/static/{aBi6EyMtLsrl7J8eH4kF7 → Q-7iofoxOEv1WX18S35of}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IMAGE-ONE-SOURCE (대표 2026-10-09 «이미지도 공통 사용·기준값 관리 — 멀티 서버가 같은 곳을 보게»).
|
|
3
|
+
*
|
|
4
|
+
* `--substrate pod` 발사는 지금 «이 트리 HEAD ≠ 노드 이미지»면 그 자리에서 굽는다 — 부모 여럿이 같은 커밋을 동시에 굽고,
|
|
5
|
+
* 노드마다 `:local` 판이 갈렸다. 운영 config `harness.imageCommit`(커밋 12자리 이상)이 있으면 발사는 굽지 않고
|
|
6
|
+
* 레지스트리의 «기준 이미지»(커밋 태그)를 당긴다. 없으면 종전 경로(HEAD 비교·굽기) 그대로다.
|
|
7
|
+
*
|
|
8
|
+
* ⛔ 레지스트리 주소·태그 규칙은 docker/harness/build.sh 의 것이다(새 규칙을 만들지 않는다):
|
|
9
|
+
* REGISTRY="${ELANOUS_BUILD_REGISTRY:-k3d-elanous-registry:${ELANOUS_FLEET_REGISTRY_PORT:-5050}}" · TAG="${COMMIT:0:12}".
|
|
10
|
+
* 풀 멤버가 `#k3d-레지스트리:포트` 를 명시했으면 그 멤버는 그 주소다(syncPoolImages 가 build.sh 에 넘기는 것과 같다).
|
|
11
|
+
* 조회·Job ref·오류 문구는 전부 «멤버마다 정해진 그 한 주소»를 쓴다.
|
|
12
|
+
* 레지스트리가 있는 풀 멤버는 원격이면 SSH, 로컬이면 이 기계에서 기준 태그를 찾는다.
|
|
13
|
+
* 레지스트리가 없는 멤버(node-c 등)와 조회할 수 없는 로컬 레지스트리는 종전 경로로 간다 — 레지스트리 미러는 범위 밖.
|
|
14
|
+
* 이미지는 표준판(elanous-harness)만 — 이 발사 경로는 lite 를 쓰지 않는다.
|
|
15
|
+
*/
|
|
16
|
+
import { spawnSync } from 'node:child_process';
|
|
17
|
+
import { debug } from '../../debug/log.js';
|
|
18
|
+
import { memberHasRegistry, registryImageRef, type RemoteRun } from './pod-pool.js';
|
|
19
|
+
|
|
20
|
+
const POD_IMAGE = 'elanous-harness';
|
|
21
|
+
|
|
22
|
+
export type PodImageSource =
|
|
23
|
+
| { readonly kind: 'pinned'; readonly ref: string; readonly commit: string; readonly registry: string }
|
|
24
|
+
| { readonly kind: 'head' };
|
|
25
|
+
|
|
26
|
+
export type PodImageSourceOutcome = 'pinned-ready' | 'pinned-waited' | 'pinned-missing' | 'head';
|
|
27
|
+
|
|
28
|
+
/** 기준 이미지가 레지스트리에 올라오기를 기다리는 기본 한도(초). */
|
|
29
|
+
export const POD_IMAGE_PIN_WAIT_SEC = 600;
|
|
30
|
+
const POD_IMAGE_PIN_POLL_MS = 15_000;
|
|
31
|
+
|
|
32
|
+
const COMMIT = /^[0-9a-f]{12,40}$/u;
|
|
33
|
+
|
|
34
|
+
/** `harness.imageCommit` 이 유효한 값인가(커밋 hex 12~40자리). */
|
|
35
|
+
export function isPodImageCommit(value: unknown): value is string {
|
|
36
|
+
return typeof value === 'string' && COMMIT.test(value.trim().toLowerCase());
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** build.sh:274 와 같은 규칙 — `ELANOUS_BUILD_REGISTRY` 다음 `k3d-elanous-registry:${ELANOUS_FLEET_REGISTRY_PORT:-5050}`. */
|
|
40
|
+
export function podImageRegistry(env: NodeJS.ProcessEnv = process.env): string {
|
|
41
|
+
// bash `${X:-d}` 는 빈 문자열도 «없음»으로 본다 — `||` 로 맞춘다.
|
|
42
|
+
return env.ELANOUS_BUILD_REGISTRY || `k3d-elanous-registry:${env.ELANOUS_FLEET_REGISTRY_PORT || '5050'}`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** build.sh:276 — 태그 = 커밋 앞 12자리. */
|
|
46
|
+
export function podImageRef(registry: string, commit: string): string {
|
|
47
|
+
return `${registry}/${POD_IMAGE}:${commit.slice(0, 12)}`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function resolvePodImageSource(
|
|
51
|
+
cfg: { harness?: { imageCommit?: unknown } } | null | undefined,
|
|
52
|
+
deps: { env?: NodeJS.ProcessEnv } = {},
|
|
53
|
+
): PodImageSource {
|
|
54
|
+
const raw = cfg?.harness?.imageCommit;
|
|
55
|
+
if (!isPodImageCommit(raw)) return { kind: 'head' };
|
|
56
|
+
const commit = raw.trim().toLowerCase();
|
|
57
|
+
const registry = podImageRegistry(deps.env ?? process.env);
|
|
58
|
+
return { kind: 'pinned', ref: podImageRef(registry, commit), commit, registry };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function podImageMissingMessage(ref: string): string {
|
|
62
|
+
return `기준 이미지 ${ref} 가 레지스트리에 없다 — 레일이 굽기 전이다`;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface PodImageMember { readonly context: string; readonly sshHost?: string; readonly registry?: string }
|
|
66
|
+
|
|
67
|
+
/** 이 멤버가 기준 이미지를 찾을 레지스트리 — 멤버 명시값, 없으면 build.sh 규칙. */
|
|
68
|
+
export function memberPinRegistry(member: PodImageMember, source: Extract<PodImageSource, { kind: 'pinned' }>): string {
|
|
69
|
+
return member.registry ?? source.registry;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** 실제 조회 — 원격은 pod-pool 의 SSH 판정, 로컬은 이 기계의 docker/curl 로 조회한다. */
|
|
73
|
+
export function podPoolImageLookup(run?: RemoteRun, localRun: (cmd: string, args: readonly string[]) => { status: number | null; stdout: string } = (cmd, args) => {
|
|
74
|
+
const r = spawnSync(cmd, [...args], { encoding: 'utf8', timeout: 10_000 });
|
|
75
|
+
return { status: r.status, stdout: r.stdout ?? '' };
|
|
76
|
+
}): {
|
|
77
|
+
hasRegistry: (member: PodImageMember & { context: string }, registry: string) => boolean;
|
|
78
|
+
lookupTag: (member: PodImageMember & { context: string }, registry: string, commit: string) => string | null;
|
|
79
|
+
} {
|
|
80
|
+
return {
|
|
81
|
+
hasRegistry: (member, registry) => {
|
|
82
|
+
if (member.sshHost) return memberHasRegistry({ capacity: 0, k3dCluster: '', ...member, registry }, run);
|
|
83
|
+
try { return localRun('docker', ['inspect', registry.split(':')[0]!]).status === 0; }
|
|
84
|
+
catch { return false; }
|
|
85
|
+
},
|
|
86
|
+
lookupTag: (member, registry, commit) => {
|
|
87
|
+
if (member.sshHost) return registryImageRef({ capacity: 0, k3dCluster: '', ...member, registry }, commit, run, `${POD_IMAGE}:local`);
|
|
88
|
+
const port = registry.slice(registry.lastIndexOf(':') + 1);
|
|
89
|
+
const tag = commit.slice(0, 12);
|
|
90
|
+
try {
|
|
91
|
+
const r = localRun('curl', ['-fsS', '--max-time', '5', `http://localhost:${port}/v2/${POD_IMAGE}/tags/list`]);
|
|
92
|
+
if (r.status !== 0) return null;
|
|
93
|
+
const tags = (JSON.parse(r.stdout) as { tags?: string[] }).tags;
|
|
94
|
+
return Array.isArray(tags) && tags.includes(tag) ? podImageRef(registry, commit) : null;
|
|
95
|
+
} catch { return null; }
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface PreparePodImageDeps<M extends PodImageMember, H> {
|
|
101
|
+
readonly cfg: { harness?: { imageCommit?: unknown } } | null | undefined;
|
|
102
|
+
readonly env?: NodeJS.ProcessEnv;
|
|
103
|
+
/** 풀 멤버(풀이 없으면 빈 배열 — 이 기계의 컨텍스트 하나). */
|
|
104
|
+
readonly members: readonly M[];
|
|
105
|
+
/** 종전 경로(HEAD 비교·굽기·노드 동기화). 기준값이 없으면 전 멤버로, 있으면 «기준 이미지를 못 쓰는» 멤버로만 부른다. */
|
|
106
|
+
readonly headPath: (members: readonly M[]) => Promise<H>;
|
|
107
|
+
/** 이 멤버의 노드에 그 레지스트리가 있나. */
|
|
108
|
+
readonly hasRegistry: (member: M, registry: string) => boolean;
|
|
109
|
+
/** 이 멤버의 그 레지스트리에 그 커밋 태그가 있으면 Pod 가 당길 ref, 없으면 null. */
|
|
110
|
+
readonly lookupTag: (member: M, registry: string, commit: string) => string | null;
|
|
111
|
+
readonly maxWaitMs?: number;
|
|
112
|
+
readonly pollMs?: number;
|
|
113
|
+
readonly sleep?: (ms: number) => Promise<void>;
|
|
114
|
+
readonly now?: () => number;
|
|
115
|
+
readonly onWait?: (pending: readonly M[], waitedMs: number) => void;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export interface PreparedPodImage<M extends PodImageMember, H> {
|
|
119
|
+
readonly source: PodImageSource;
|
|
120
|
+
readonly outcome: PodImageSourceOutcome;
|
|
121
|
+
/** 기준 이미지를 당기는 멤버 — `imageRef` 가 Job 이미지다. */
|
|
122
|
+
readonly pinned: ReadonlyArray<M & { imageRef: string }>;
|
|
123
|
+
/** 레지스트리는 있는데 한도 안에 태그가 안 올라온 멤버와 그 멤버가 찾던 ref. */
|
|
124
|
+
readonly missing: ReadonlyArray<{ member: M; ref: string }>;
|
|
125
|
+
/** 종전 경로를 탄 멤버(레지스트리 없음 · 로컬 태그 조회 실패). */
|
|
126
|
+
readonly unpinned: readonly M[];
|
|
127
|
+
/** 종전 경로의 결과 — 부르지 않았으면 null. */
|
|
128
|
+
readonly head: H | null;
|
|
129
|
+
readonly waitedMs: number;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* 발사 이미지를 준비한다. 기준값이 없으면 `headPath(members)` 하나만 부른다(종전 동작 그대로).
|
|
134
|
+
* 기준값이 있으면: 레지스트리가 있는 멤버는 태그를 기다려 당기고(굽지 않는다), 나머지 멤버만 종전 경로로 보낸다.
|
|
135
|
+
* 원격 레지스트리 멤버 «전부» 태그가 끝내 없으면 outcome `pinned-missing` — 종전 경로도 부르지 않는다(호출부가 exit 2).
|
|
136
|
+
*/
|
|
137
|
+
export async function preparePodImage<M extends PodImageMember, H>(deps: PreparePodImageDeps<M, H>): Promise<PreparedPodImage<M, H>> {
|
|
138
|
+
const source = resolvePodImageSource(deps.cfg, { env: deps.env });
|
|
139
|
+
if (source.kind === 'head') {
|
|
140
|
+
return { source, outcome: 'head', pinned: [], missing: [], unpinned: deps.members, head: await deps.headPath(deps.members), waitedMs: 0 };
|
|
141
|
+
}
|
|
142
|
+
const localRefs = new Map<string, string>();
|
|
143
|
+
const registryMembers = deps.members.filter((m) => {
|
|
144
|
+
const registry = memberPinRegistry(m, source);
|
|
145
|
+
if (!deps.hasRegistry(m, registry)) return false;
|
|
146
|
+
if (m.sshHost) return true;
|
|
147
|
+
// A local registry without the requested tag (or an unreadable one) can still use the old build path.
|
|
148
|
+
const ref = deps.lookupTag(m, registry, source.commit);
|
|
149
|
+
if (!ref) return false;
|
|
150
|
+
localRefs.set(m.context, ref);
|
|
151
|
+
return true;
|
|
152
|
+
});
|
|
153
|
+
const unpinned = deps.members.filter((m) => !registryMembers.includes(m));
|
|
154
|
+
if (registryMembers.length === 0) {
|
|
155
|
+
// 기준 이미지를 당길 멤버가 없다(풀 없음 · 레지스트리 없는 멤버뿐) — 종전 경로.
|
|
156
|
+
return { source, outcome: 'head', pinned: [], missing: [], unpinned, head: await deps.headPath(unpinned), waitedMs: 0 };
|
|
157
|
+
}
|
|
158
|
+
const now = deps.now ?? Date.now;
|
|
159
|
+
const sleep = deps.sleep ?? ((ms: number) => new Promise<void>((done) => setTimeout(done, ms)));
|
|
160
|
+
const maxWaitMs = Math.max(0, deps.maxWaitMs ?? POD_IMAGE_PIN_WAIT_SEC * 1000);
|
|
161
|
+
const pollMs = Math.max(1, deps.pollMs ?? POD_IMAGE_PIN_POLL_MS);
|
|
162
|
+
const found = new Map<string, string>();
|
|
163
|
+
const t0 = now();
|
|
164
|
+
let pending = registryMembers;
|
|
165
|
+
let slept = false;
|
|
166
|
+
for (;;) {
|
|
167
|
+
pending = pending.filter((m) => {
|
|
168
|
+
const ref = localRefs.get(m.context) ?? deps.lookupTag(m, memberPinRegistry(m, source), source.commit);
|
|
169
|
+
if (ref) found.set(m.context, ref);
|
|
170
|
+
return !ref;
|
|
171
|
+
});
|
|
172
|
+
const waited = now() - t0;
|
|
173
|
+
if (pending.length === 0 || waited >= maxWaitMs) break;
|
|
174
|
+
deps.onWait?.(pending, waited);
|
|
175
|
+
slept = true;
|
|
176
|
+
await sleep(Math.min(pollMs, maxWaitMs - waited));
|
|
177
|
+
}
|
|
178
|
+
const waitedMs = now() - t0;
|
|
179
|
+
const pinned = registryMembers.filter((m) => found.has(m.context)).map((m) => ({ ...m, imageRef: found.get(m.context)! }));
|
|
180
|
+
const missing = pending.map((member) => ({ member, ref: podImageRef(memberPinRegistry(member, source), source.commit) }));
|
|
181
|
+
if (pinned.length === 0) {
|
|
182
|
+
return { source, outcome: 'pinned-missing', pinned, missing, unpinned, head: null, waitedMs };
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
source,
|
|
186
|
+
outcome: slept ? 'pinned-waited' : 'pinned-ready',
|
|
187
|
+
pinned,
|
|
188
|
+
missing,
|
|
189
|
+
unpinned,
|
|
190
|
+
head: unpinned.length > 0 ? await deps.headPath(unpinned) : null,
|
|
191
|
+
waitedMs,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* 기준값 경로의 `image-source` 사건 — 발사 호출부(index.ts)가 부르는 단 하나의 기록처.
|
|
197
|
+
* 이 기계의 로컬 멤버(sshHost 없음)가 기준 이미지를 당겼으면 `local: true` 를 싣는다.
|
|
198
|
+
*/
|
|
199
|
+
export function logPinnedImageSource<M extends PodImageMember, H>(
|
|
200
|
+
prepared: PreparedPodImage<M, H>,
|
|
201
|
+
ctx: { readonly ref: string; readonly headCommit: string | null },
|
|
202
|
+
): void {
|
|
203
|
+
if (prepared.source.kind !== 'pinned') return;
|
|
204
|
+
const local = prepared.pinned.some((m) => !m.sshHost);
|
|
205
|
+
debug.log('self-implement.pod', 'image-source', {
|
|
206
|
+
kind: prepared.source.kind, ref: ctx.ref, pinRef: prepared.source.ref, headCommit: ctx.headCommit, waitedMs: prepared.waitedMs, outcome: prepared.outcome,
|
|
207
|
+
...(local ? { local: true } : {}),
|
|
208
|
+
pinned: prepared.pinned.map((m) => ({ context: m.context, imageRef: m.imageRef })),
|
|
209
|
+
missing: prepared.missing.map((x) => ({ context: x.member.context, ref: x.ref })),
|
|
210
|
+
unpinned: prepared.unpinned.map((m) => m.context),
|
|
211
|
+
});
|
|
212
|
+
}
|
|
@@ -8,7 +8,14 @@ import { dlopen } from 'bun:ffi';
|
|
|
8
8
|
import { hostLeaseBaseDir, type PendingJobIdentity } from '../../pod-lease/host-lease.js';
|
|
9
9
|
|
|
10
10
|
/** The harness Pod's container requests (self-implement-pod Job manifest). Lives here so admission reuses the real request. */
|
|
11
|
-
|
|
11
|
+
// POD-CPU-REQUEST-RIGHTSIZE(10-09 · OP 실측): 하니스 Pod 실사용은 대부분 100m 미만(LLM 응답 대기)인데 요청 1 CPU 를
|
|
12
|
+
// 잡아 node-b 32코어가 «요청 31/32»로 막혔다(실사용 4.6코어 · 14%). 요청은 실사용 근처로, 한도(4)는 그대로 — 몰릴 때는 한도까지 쓴다.
|
|
13
|
+
// 게이트·pod-command 기본은 pod-command-job.ts 의 POD_COMMAND_CPU_REQUEST_DEFAULT(1)로 따로 둔다(테스트 조각은 CPU 를 실제로 쓴다).
|
|
14
|
+
export const POD_CHILD_REQUESTS = { cpu: '250m', memory: '6Gi' } as const;
|
|
15
|
+
/** Finished harness and command Jobs release their Pod slots after 30 minutes. */
|
|
16
|
+
export const POD_JOB_TTL_AFTER_FINISHED_SECONDS = 1800;
|
|
17
|
+
/** 게이트가 돌 때 하니스 «한 자리»로 셈하는 CPU — 요청(250m)보다 보수적으로 1 코어를 잡아 게이트 조각의 CPU 를 지킨다(GATE-RESERVE-AUTO 셈은 그대로). */
|
|
18
|
+
export const HARNESS_SLOT_CPU_BESIDE_GATE = '1';
|
|
12
19
|
/** POD-ADMIT-BY-USAGE — observed usage is scaled by this before it reserves memory. */
|
|
13
20
|
const POD_USAGE_HEADROOM = 1.5;
|
|
14
21
|
/** POD-ADMIT-BY-USAGE — a node keeps at least this share of allocatable memory free (real usage and bookkeeping). */
|
|
@@ -33,6 +40,8 @@ export function leaseKubectl(args: readonly string[], input?: string): { status:
|
|
|
33
40
|
export interface PodLeaseMember {
|
|
34
41
|
context: string;
|
|
35
42
|
capacity: number;
|
|
43
|
+
/** POD-POOL-UNCAPPED-SPEC — `capacity` came from the spec default (`:N` absent), not from the operator. */
|
|
44
|
+
capacityDefaulted?: boolean;
|
|
36
45
|
running: number | null;
|
|
37
46
|
pending: number | null;
|
|
38
47
|
/** Job names of Pending harness Pods already included in pending (used to avoid counting the same Job's host lease twice). */
|
|
@@ -87,7 +96,12 @@ export interface PoolLeaseMeasure {
|
|
|
87
96
|
|
|
88
97
|
export interface PoolLeaseRecommendation {
|
|
89
98
|
recommended: number | null;
|
|
90
|
-
|
|
99
|
+
/**
|
|
100
|
+
* What stopped admission: `memory` = node memory headroom · `capacity` = the pool slot budget ·
|
|
101
|
+
* `pool-cap` = POD-POOL-UNCAPPED-SPEC — a member whose cap came from the spec default (`:N` absent → 2) is
|
|
102
|
+
* already at/over that cap, so the pool spec itself (not memory, not a configured budget) is the bound.
|
|
103
|
+
*/
|
|
104
|
+
limitedBy: 'capacity' | 'memory' | 'pool-cap' | null;
|
|
91
105
|
reason: string | null;
|
|
92
106
|
capacitySlots: number | null;
|
|
93
107
|
memorySlots: number | null;
|
|
@@ -207,6 +221,7 @@ function runningLimit(spec: Record<string, unknown>): number | null {
|
|
|
207
221
|
/** A short-lived Pod proves cluster DNS from inside the same namespace as harness Jobs. Image pull failures are not DNS failures. */
|
|
208
222
|
export type PoolDnsProbe = 'ready' | 'dns' | 'image' | 'unknown';
|
|
209
223
|
const DNS_CACHE_MS = 10 * 60_000;
|
|
224
|
+
const DNS_STALE_OK_MS = 30 * 60_000;
|
|
210
225
|
const DNS_LOCK_WAIT_MS = 6 * 60_000;
|
|
211
226
|
// flock is released by the kernel on process exit, including a crash before any cache write.
|
|
212
227
|
// The lock file is never unlinked: unlinking it would let waiters lock different inodes.
|
|
@@ -232,6 +247,17 @@ export function probePoolDns(context: string, kubectl: PoolKubectl = leaseKubect
|
|
|
232
247
|
return record?.result === 'ready' && typeof record.at === 'number' &&
|
|
233
248
|
now() >= record.at && now() - record.at < DNS_CACHE_MS;
|
|
234
249
|
};
|
|
250
|
+
const withStaleReady = (result: PoolDnsProbe): PoolDnsProbe => {
|
|
251
|
+
if (result !== 'unknown') return result;
|
|
252
|
+
const record = readRecord(cache);
|
|
253
|
+
const readyAgeMs = typeof record?.at === 'number' ? now() - record.at : NaN;
|
|
254
|
+
if (record?.result === 'ready' &&
|
|
255
|
+
Number.isFinite(readyAgeMs) && readyAgeMs >= 0 && readyAgeMs < DNS_STALE_OK_MS) {
|
|
256
|
+
debug.log('pod.lease', 'dns-probe-stale-ready', { context, readyAgeMs });
|
|
257
|
+
return 'ready';
|
|
258
|
+
}
|
|
259
|
+
return result;
|
|
260
|
+
};
|
|
235
261
|
if (cached()) return 'ready';
|
|
236
262
|
const previous = readRecord(outcome);
|
|
237
263
|
try { mkdirSync(dir, { recursive: true, mode: 0o700 }); } catch { return 'unknown'; }
|
|
@@ -251,31 +277,34 @@ export function probePoolDns(context: string, kubectl: PoolKubectl = leaseKubect
|
|
|
251
277
|
while (!held) {
|
|
252
278
|
if (cached()) return 'ready';
|
|
253
279
|
const shared = sharedFailure();
|
|
254
|
-
if (shared) return shared;
|
|
280
|
+
if (shared) return withStaleReady(shared);
|
|
255
281
|
if (dnsFlock(fd, 2 | 4) === 0) { held = true; break; } // LOCK_EX | LOCK_NB
|
|
256
282
|
if (Date.now() >= deadline) return 'unknown';
|
|
257
283
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 100);
|
|
258
284
|
}
|
|
259
285
|
if (cached()) return 'ready';
|
|
260
286
|
const shared = sharedFailure();
|
|
261
|
-
if (shared) return shared;
|
|
262
|
-
const result = runPoolDnsProbe(context, kubectl);
|
|
287
|
+
if (shared) return withStaleReady(shared);
|
|
288
|
+
const result = runPoolDnsProbe(context, kubectl, now);
|
|
263
289
|
const destination = result === 'ready' ? cache : outcome;
|
|
264
290
|
const temp = `${destination}.${randomUUID()}.tmp`;
|
|
265
291
|
try {
|
|
266
292
|
writeFileSync(temp, JSON.stringify({ result, at: now(), generation: randomUUID() }), { mode: 0o600 });
|
|
267
293
|
renameSync(temp, destination);
|
|
268
294
|
} catch { try { rmSync(temp, { force: true }); } catch { /* best-effort cleanup */ } }
|
|
269
|
-
return result;
|
|
295
|
+
return withStaleReady(result);
|
|
270
296
|
} finally {
|
|
271
297
|
if (held) dnsFlock(fd, 8); // LOCK_UN
|
|
272
298
|
closeSync(fd);
|
|
273
299
|
}
|
|
274
300
|
}
|
|
275
301
|
|
|
276
|
-
|
|
302
|
+
const DNS_PROBE_READY = /\bName:\s*kubernetes\.default\.svc\.cluster\.local\b/u;
|
|
303
|
+
|
|
304
|
+
function runPoolDnsProbe(context: string, kubectl: PoolKubectl, now: () => number): PoolDnsProbe {
|
|
277
305
|
const name = `elanous-dns-${randomUUID().slice(0, 12)}`;
|
|
278
306
|
const base = ['--context', context, '--request-timeout=10s'];
|
|
307
|
+
let cleanup = false;
|
|
279
308
|
try {
|
|
280
309
|
const core = kubectl([...base, '-n', 'kube-system', 'get', 'deployment', 'coredns', '-o', 'json']);
|
|
281
310
|
if (core.status !== 0) return 'unknown';
|
|
@@ -283,12 +312,61 @@ function runPoolDnsProbe(context: string, kubectl: PoolKubectl): PoolDnsProbe {
|
|
|
283
312
|
const spec = object(deployment?.spec);
|
|
284
313
|
const status = object(deployment?.status);
|
|
285
314
|
if (typeof spec?.replicas !== 'number' || spec.replicas < 1 || status?.readyReplicas !== spec.replicas) return 'dns';
|
|
315
|
+
const listed = kubectl([...base, '-n', 'elanous-test', 'get', 'pods', '-l', 'elanous.probe=dns', '-o', 'json']);
|
|
316
|
+
if (listed.status !== 0) return 'unknown';
|
|
317
|
+
const listedPods = items(JSON.parse(listed.stdout));
|
|
318
|
+
if (!listedPods) return 'unknown';
|
|
319
|
+
const probes = listedPods.filter((pod) => {
|
|
320
|
+
const metadata = object(pod.metadata);
|
|
321
|
+
return object(metadata?.labels)?.['elanous.probe'] === 'dns' &&
|
|
322
|
+
(metadata?.namespace === undefined || metadata.namespace === 'elanous-test');
|
|
323
|
+
});
|
|
324
|
+
let reaped = 0;
|
|
325
|
+
for (const probe of probes) {
|
|
326
|
+
const metadata = object(probe.metadata);
|
|
327
|
+
const name = metadata?.name;
|
|
328
|
+
const deletedAt = metadata?.deletionTimestamp;
|
|
329
|
+
if (typeof name !== 'string' || !/^[a-z0-9]([-a-z0-9]*[a-z0-9])?$/.test(name) ||
|
|
330
|
+
typeof deletedAt !== 'string' || !Number.isFinite(Date.parse(deletedAt)) ||
|
|
331
|
+
now() - Date.parse(deletedAt) <= 60_000) continue;
|
|
332
|
+
try {
|
|
333
|
+
if (kubectl([...base, '-n', 'elanous-test', 'delete', 'pods', '-l', 'elanous.probe=dns', `--field-selector=metadata.name=${name}`, '--grace-period=0', '--force', '--ignore-not-found=true', '--wait=false']).status === 0) reaped++;
|
|
334
|
+
} catch { /* Reaping is best-effort; the crowded reading still prevents a new probe. */ }
|
|
335
|
+
}
|
|
336
|
+
if (reaped) debug.log('pod.lease', 'dns-probe-stale-reaped', { context, count: reaped });
|
|
337
|
+
// 🩸 10-09 22:1x: node-b had 20 Completed probes left over from older releases (and 7 Terminating). Counting every
|
|
338
|
+
// labelled Pod would make the node «crowded» forever and admission would stay 0 — only live probes are crowding.
|
|
339
|
+
// Finished probes are removed here so older leftovers drain too.
|
|
340
|
+
let finished = 0;
|
|
341
|
+
const live = probes.filter((probe) => {
|
|
342
|
+
const metadata = object(probe.metadata);
|
|
343
|
+
if (metadata?.deletionTimestamp !== undefined) return false;
|
|
344
|
+
const phase = object(probe.status)?.phase;
|
|
345
|
+
if (phase !== 'Succeeded' && phase !== 'Failed') return true;
|
|
346
|
+
const name = metadata?.name;
|
|
347
|
+
if (typeof name === 'string' && /^[a-z0-9]([-a-z0-9]*[a-z0-9])?$/.test(name)) {
|
|
348
|
+
try {
|
|
349
|
+
if (kubectl([...base, '-n', 'elanous-test', 'delete', 'pods', '-l', 'elanous.probe=dns', `--field-selector=metadata.name=${name}`, '--ignore-not-found=true', '--wait=false']).status === 0) finished++;
|
|
350
|
+
} catch { /* best-effort: a finished probe never crowds */ }
|
|
351
|
+
}
|
|
352
|
+
return false;
|
|
353
|
+
});
|
|
354
|
+
if (finished) debug.log('pod.lease', 'dns-probe-finished-reaped', { context, count: finished });
|
|
355
|
+
if (live.length >= 3) {
|
|
356
|
+
debug.log('pod.lease', 'dns-probe-skipped-crowded', { context, existing: live.length });
|
|
357
|
+
return 'unknown';
|
|
358
|
+
}
|
|
359
|
+
cleanup = true;
|
|
286
360
|
const manifest = JSON.stringify({ apiVersion: 'v1', kind: 'Pod', metadata: { name, namespace: 'elanous-test', labels: { 'elanous.probe': 'dns' } }, spec: {
|
|
287
|
-
restartPolicy: 'Never', activeDeadlineSeconds:
|
|
288
|
-
|
|
361
|
+
restartPolicy: 'Never', activeDeadlineSeconds: 120, automountServiceAccountToken: false,
|
|
362
|
+
// 🩸 10-09 18:2x: 노드 CPU «요청» 100%(32/32) 이면 10m 탐침도 못 떠 측정이 «dns probe unknown» → 승인 추천 null → 전 발사 정지(교착).
|
|
363
|
+
// 탐침은 CPU 요청 0(명시 · 안 쓰면 k8s 가 한도 100m 를 요청으로 복사)으로 «꽉 찬 노드에도 뜨게» 한다.
|
|
364
|
+
containers: [{ name: 'dns', image: 'busybox:1.36', command: ['nslookup', 'kubernetes.default.svc.cluster.local'], resources: { requests: { cpu: '0', memory: '16Mi' }, limits: { cpu: '100m', memory: '64Mi' } } }],
|
|
289
365
|
} });
|
|
290
366
|
if (kubectl([...base, 'create', '-f', '-'], manifest).status !== 0) return 'unknown';
|
|
291
|
-
|
|
367
|
+
// 🩸 10-09 19:3x: 부하 104 노드에선 «배치됨»(PodScheduled=True) 뒤 컨테이너 생성만 30초를 넘겼다 → 30초 wait 가 Pending 을 보고 unknown.
|
|
368
|
+
// 기다림 90초 · Pod 기한 120초. 성공은 DNS_CACHE_MS 동안 공유 캐시라 매 측정이 이 시간을 물지 않는다.
|
|
369
|
+
const waited = kubectl([...base, '-n', 'elanous-test', 'wait', '--for=jsonpath={.status.phase}=Succeeded', `pod/${name}`, '--timeout=90s']);
|
|
292
370
|
const pod = kubectl([...base, '-n', 'elanous-test', 'get', `pod/${name}`, '-o', 'json']);
|
|
293
371
|
if (pod.status !== 0) return 'unknown';
|
|
294
372
|
const state = object(JSON.parse(pod.stdout));
|
|
@@ -296,14 +374,24 @@ function runPoolDnsProbe(context: string, kubectl: PoolKubectl): PoolDnsProbe {
|
|
|
296
374
|
const statuses = object(state?.status)?.containerStatuses;
|
|
297
375
|
if (Array.isArray(statuses) && statuses.some((c) =>
|
|
298
376
|
['ErrImagePull', 'ImagePullBackOff', 'InvalidImageName'].includes(String(object(object(object(c)?.state)?.waiting)?.reason)))) return 'image';
|
|
377
|
+
// 🩸 10-09 18:5x: 부하 걸린 노드(load 78)에선 kubelet 이 «끝난» 탐침의 phase 를 30초 넘게 Running 으로 둔다
|
|
378
|
+
// (nslookup 이 답을 찍은 뒤에도 · `timeout 8 nslookup` 도 46초 Running). phase 만 보면 측정이 «unknown» → 승인 0.
|
|
379
|
+
// Running 이면 로그를 읽어 해석 성공(이름·주소 줄)이 이미 찍혔는지 본다 — 찍혔으면 DNS 는 된다.
|
|
380
|
+
if (phase === 'Running') {
|
|
381
|
+
const early = kubectl([...base, '-n', 'elanous-test', 'logs', `pod/${name}`]);
|
|
382
|
+
if (early.status === 0 && DNS_PROBE_READY.test(early.stdout) && /\bAddress(?:es)?:\s*\S+/u.test(early.stdout)) {
|
|
383
|
+
debug.log('pod.lease', 'dns-probe-ready-by-log', { context, phase });
|
|
384
|
+
return 'ready';
|
|
385
|
+
}
|
|
386
|
+
}
|
|
299
387
|
if (phase !== 'Succeeded' && phase !== 'Failed') return 'unknown';
|
|
300
388
|
if (waited.status !== 0 && phase !== 'Failed') return 'unknown';
|
|
301
389
|
const log = kubectl([...base, '-n', 'elanous-test', 'logs', `pod/${name}`]);
|
|
302
390
|
if (log.status !== 0) return 'unknown';
|
|
303
391
|
if (phase === 'Failed') return /can't resolve|server can't find|connection timed out|no servers could be reached|NXDOMAIN|SERVFAIL/iu.test(log.stdout) ? 'dns' : 'unknown';
|
|
304
|
-
return
|
|
392
|
+
return DNS_PROBE_READY.test(log.stdout) && /\bAddress(?:es)?:\s*\S+/u.test(log.stdout) ? 'ready' : 'dns';
|
|
305
393
|
} catch { return 'unknown'; }
|
|
306
|
-
finally { try { kubectl([...base, '-n', 'elanous-test', 'delete', `pod/${name}`, '--ignore-not-found=true', '--wait=false']); } catch { /* best-effort cleanup */ } }
|
|
394
|
+
finally { if (cleanup) { try { kubectl([...base, '-n', 'elanous-test', 'delete', `pod/${name}`, '--ignore-not-found=true', '--wait=false']); } catch { /* best-effort cleanup */ } } }
|
|
307
395
|
}
|
|
308
396
|
|
|
309
397
|
/** Each cluster reads resource reservations; DNS requires a bounded disposable Pod. */
|
|
@@ -311,7 +399,7 @@ export function measurePoolLease(members: readonly PodPoolMember[], deps: { kube
|
|
|
311
399
|
const kubectl = deps.kubectl ?? leaseKubectl;
|
|
312
400
|
return { members: members.map((member) => {
|
|
313
401
|
const base = ['--context', member.context, '--request-timeout=10s'];
|
|
314
|
-
const result: PodLeaseMember = { context: member.context, capacity: member.capacity, running: null, pending: null, unleasedRunning: null, memoryLimitBytes: null, allocatableMemoryBytes: null, allocatableCpuMillicores: null, availableMemoryByNodeBytes: null, reason: null };
|
|
402
|
+
const result: PodLeaseMember = { context: member.context, capacity: member.capacity, ...(member.capacityDefaulted ? { capacityDefaulted: true } : {}), running: null, pending: null, unleasedRunning: null, memoryLimitBytes: null, allocatableMemoryBytes: null, allocatableCpuMillicores: null, availableMemoryByNodeBytes: null, reason: null };
|
|
315
403
|
try {
|
|
316
404
|
const nodes = kubectl([...base, 'get', 'nodes', '-o', 'json']);
|
|
317
405
|
const jobs = kubectl([...base, '-n', 'elanous-test', 'get', 'jobs', '-l', 'elanous.substrate=pod', '-o', 'json']);
|
|
@@ -534,7 +622,7 @@ export function measurePoolLease(members: readonly PodPoolMember[], deps: { kube
|
|
|
534
622
|
*/
|
|
535
623
|
export function harnessCpuSlotsBesideGate(measure: PoolLeaseMeasure): number | null | undefined {
|
|
536
624
|
if (!measure.members.some((m) => (m.gateJobs ?? 0) > 0)) return undefined;
|
|
537
|
-
const perPod = quantity(
|
|
625
|
+
const perPod = quantity(HARNESS_SLOT_CPU_BESIDE_GATE, 'cpu')!;
|
|
538
626
|
let slots = 0;
|
|
539
627
|
for (const m of measure.members) {
|
|
540
628
|
if (m.gateJobs == null || m.gateUnboundJobs == null || m.cpuFreeByNodeMillicores == null || m.pendingHarnessCpuMillicores == null) return null;
|
|
@@ -554,7 +642,7 @@ export function recommendConcurrency(measure: PoolLeaseMeasure, options: { capac
|
|
|
554
642
|
if (goalBytes === null || goalBytes <= 0) return { ...empty, reason: '측정 불가: perGoalMemory' };
|
|
555
643
|
const admission = admissionByUsage(measure, goalBytes, options.perGoalMemory !== undefined);
|
|
556
644
|
if (!measure.members.length || measure.members.some((m) => m.running === null || m.pending === null || m.unleasedRunning === null || m.allocatableMemoryBytes === null || m.memoryLimitBytes === null || m.allocatableCpuMillicores === null || m.availableMemoryByNodeBytes === null)) {
|
|
557
|
-
debug.log('pod-lease', 'admit-by-usage', { samples: admission.samples, p95Bytes: admission.p95Bytes, admitBytes: admission.admitBytes, fallback: admission.fallback, recommended: null, limitedBy: null });
|
|
645
|
+
debug.log('pod-lease', 'admit-by-usage', { samples: admission.samples, p95Bytes: admission.p95Bytes, admitBytes: admission.admitBytes, fallback: admission.fallback, recommended: null, limitedBy: null, capacity: options.capacity, capacitySlots: null, memorySlots: null, running: null, pending: null });
|
|
558
646
|
return { ...empty, reason: `측정 불가: cluster${measure.members.map((m) => m.reason ? ` ${m.context}: ${m.reason}` : '').join('')}` };
|
|
559
647
|
}
|
|
560
648
|
if (!Number.isSafeInteger(options.capacity) || options.capacity < 0) return { ...empty, reason: '측정 불가: capacity' };
|
|
@@ -599,9 +687,14 @@ export function recommendConcurrency(measure: PoolLeaseMeasure, options: { capac
|
|
|
599
687
|
Math.floor((m.allocatableMemoryBytes! - m.memoryLimitBytes!) / liteBytes))), 0);
|
|
600
688
|
const memoryBound = Math.min(memorySlots, placeableSlots);
|
|
601
689
|
const recommended = Math.max(0, Math.min(capacitySlots, memoryBound) - eligibleUnleasedRunning);
|
|
602
|
-
|
|
690
|
+
// POD-POOL-UNCAPPED-SPEC — `capacity ≤ occupied` on a member whose cap is the spec default (`:N` absent → 2) means
|
|
691
|
+
// the «uncapped» pool spec is what admits nothing. Name it `pool-cap`, not the generic `capacity`, so a launch that
|
|
692
|
+
// sits on `recommended=0` says in its first admit line that the pool spec never declared a cap of its own.
|
|
693
|
+
const defaultCapFull = capacitySlots <= 0 && measure.members.some((m) => m.capacityDefaulted === true
|
|
694
|
+
&& m.reason !== 'dns' && m.running !== null && m.pending !== null && m.running + m.pending >= m.capacity);
|
|
695
|
+
const limitedBy: PoolLeaseRecommendation['limitedBy'] = memoryBound < capacitySlots ? 'memory' : defaultCapFull ? 'pool-cap' : 'capacity';
|
|
603
696
|
if (unleasedRunning > 0) debug.log('pod-lease', 'unleased', { unleasedRunning, running, recommended });
|
|
604
|
-
debug.log('pod-lease', 'admit-by-usage', { samples: admission.samples, p95Bytes: admission.p95Bytes, admitBytes: admission.admitBytes, fallback: admission.fallback, recommended, limitedBy });
|
|
697
|
+
debug.log('pod-lease', 'admit-by-usage', { samples: admission.samples, p95Bytes: admission.p95Bytes, admitBytes: admission.admitBytes, fallback: admission.fallback, recommended, limitedBy, capacity: options.capacity, capacitySlots, memorySlots, running, pending });
|
|
605
698
|
return { recommended, limitedBy, reason: measure.members.some((m) => m.reason === 'dns') ? 'dns' : null, capacitySlots, memorySlots, liteMemorySlots, placeableSlots, accountSlots, running, pending, pendingJobs, unleasedRunning, admission };
|
|
606
699
|
}
|
|
607
700
|
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { dirname } from 'node:path';
|
|
2
3
|
import { gunzipSync } from 'node:zlib';
|
|
3
4
|
import { debug } from '../../debug/log.js';
|
|
4
|
-
import {
|
|
5
|
+
import { recordFailureEvent } from '../../self-implement/heal-intake.js';
|
|
6
|
+
import { loadRunLedger, runLedgerDir, runLedgerPath, TRAILING_BOOKKEEPING_EVENTS } from '../../self-implement/run-ledger.js';
|
|
5
7
|
import { ledgerLineToLogEvent } from './pod-ledger-events.js';
|
|
6
8
|
import { defaultPodReemit } from './pod-ledger-prod-sink.js';
|
|
7
9
|
|
|
@@ -12,6 +14,8 @@ type ParseResult = Ledger[] | { error: Incomplete[]; ledgers: Ledger[] };
|
|
|
12
14
|
const NON_PROGRESS_EVENTS = new Set(['progress-delivery-outcome']);
|
|
13
15
|
const REEMIT_EXCLUDED_PREFIX = 'progress-delivery';
|
|
14
16
|
type Emit = (category: string, event: string, data: Record<string, unknown>) => void;
|
|
17
|
+
/** Heal-loop starter seam — defaults to the real `startHealLoop` inside `recordFailureEvent`. */
|
|
18
|
+
type StartHealLoop = Parameters<typeof recordFailureEvent>[2];
|
|
15
19
|
|
|
16
20
|
/** Reemit only complete ledger records; offsets name the first byte of each line in the Pod ledger. */
|
|
17
21
|
function reemitLedgerLines(jsonl: string, runId: string, startOffset: number, emit: Emit): number {
|
|
@@ -91,15 +95,37 @@ export function parsePodLedgerChunks(logs: string): ParseResult {
|
|
|
91
95
|
return error.length ? { error, ledgers } : ledgers;
|
|
92
96
|
}
|
|
93
97
|
|
|
98
|
+
function intakeCollectedFailure(runId: string, dir: string, log: Emit, startHealLoop?: StartHealLoop): void {
|
|
99
|
+
let ledger: ReturnType<typeof loadRunLedger>;
|
|
100
|
+
try { ledger = loadRunLedger(runId, dir); }
|
|
101
|
+
catch { return; }
|
|
102
|
+
if (!ledger) return;
|
|
103
|
+
let terminalIndex = ledger.length - 1;
|
|
104
|
+
while (terminalIndex >= 0 && ledger[terminalIndex]?.event !== 'run-status') terminalIndex -= 1;
|
|
105
|
+
const terminal = ledger[terminalIndex];
|
|
106
|
+
if (terminal?.data.runStatus !== 'failed' || !terminal.timestamp || !Number.isFinite(Date.parse(terminal.timestamp))
|
|
107
|
+
|| ledger.slice(terminalIndex + 1).some(entry => !TRAILING_BOOKKEEPING_EVENTS.includes(entry.event as typeof TRAILING_BOOKKEEPING_EVENTS[number]))) return;
|
|
108
|
+
try {
|
|
109
|
+
// POD-HEAL-HOST — the recovered Pod failure is recorded into the HOST universe (`dirname(dir)`),
|
|
110
|
+
// so the host heal loop triages it (harvest + narrowed relaunch) instead of dying inside the Pod.
|
|
111
|
+
recordFailureEvent({ source: 'harness-run', kind: 'pod-run', ref: runId,
|
|
112
|
+
summary: `Pod run ${runId} failed`, at: terminal.timestamp }, dirname(dir), startHealLoop);
|
|
113
|
+
} catch (error) {
|
|
114
|
+
log('self-implement.pod', 'heal-intake-failed', { runId, reason: error instanceof Error ? error.message : String(error) });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
94
118
|
/** Never replace a host ledger, even if another collector races this one —
|
|
95
119
|
* except a ledger that THIS run's live follower created (`replace`): that one is a partial copy and the final is complete. */
|
|
96
120
|
export function collectPodLedgers(
|
|
97
121
|
logs: string,
|
|
98
|
-
{ dir = runLedgerDir(), log = defaultPodReemit(), emit = defaultPodReemit(), replace = new Set<string>() }: {
|
|
122
|
+
{ dir = runLedgerDir(), log = defaultPodReemit(), emit = defaultPodReemit(), replace = new Set<string>(), startHealLoop }: {
|
|
99
123
|
dir?: string;
|
|
100
124
|
log?: (category: string, event: string, data: Record<string, unknown>) => void;
|
|
101
125
|
emit?: Emit;
|
|
102
126
|
replace?: ReadonlySet<string>;
|
|
127
|
+
/** Which starter receives a recovered Pod failure (POD-HEAL-HOST 판정선 measurement); default = real `startHealLoop`. */
|
|
128
|
+
startHealLoop?: StartHealLoop;
|
|
103
129
|
} = {},
|
|
104
130
|
): void {
|
|
105
131
|
const parsed = parsePodLedgerChunks(logs);
|
|
@@ -116,13 +142,16 @@ export function collectPodLedgers(
|
|
|
116
142
|
const newLines = Buffer.from(jsonl).subarray(previousBytes).toString('utf8');
|
|
117
143
|
reemitLedgerLines(newLines, runId, previousBytes, emit);
|
|
118
144
|
log('self-implement.pod', 'ledger-collected', { runId, lines: jsonl.split('\n').filter(Boolean).length, bytes: Buffer.byteLength(jsonl) });
|
|
145
|
+
intakeCollectedFailure(runId, dir, log, startHealLoop);
|
|
119
146
|
} catch (e) {
|
|
120
147
|
if ((e as NodeJS.ErrnoException).code === 'EEXIST') {
|
|
121
148
|
// A reattached Job may have already delivered this exact complete snapshot.
|
|
122
149
|
// An unrelated host ledger (or a partial copy) must still remain untouched.
|
|
123
150
|
try {
|
|
124
151
|
const existing = readFileSync(runLedgerPath(runId, dir));
|
|
125
|
-
|
|
152
|
+
const identical = existing.equals(Buffer.from(jsonl));
|
|
153
|
+
log('self-implement.pod', identical ? 'ledger-collect-already-complete' : 'ledger-collect-skipped', { runId, reason: 'exists' });
|
|
154
|
+
if (identical) intakeCollectedFailure(runId, dir, log, startHealLoop);
|
|
126
155
|
} catch (readError) {
|
|
127
156
|
log('self-implement.pod', 'ledger-collect-incomplete', { runId, reason: readError instanceof Error ? readError.message : String(readError) });
|
|
128
157
|
}
|