elanous 0.2.23 → 0.2.25
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/chunks/1027.a0f04663d24c2779.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1038.676007ae505822e7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1113.a37eadf7f740d955.js +1 -0
- package/apps/pwa/out/_next/static/chunks/112-fa7c526c20ce1405.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1144.5fa1fa1d48dbcd12.js +36 -0
- package/apps/pwa/out/_next/static/chunks/1146.80d15ea7c6e6ee95.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1157.f08bf2b70915e176.js +148 -0
- package/apps/pwa/out/_next/static/chunks/119b405e.8fdd0e44d1ff37aa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1296.04560d98e73fbf51.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1314-f604ba013ec535b0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1454-37fd16f479f5554a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1464.f3209af0d8ae4ff2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8532.a86e99acf9b761cb.js → 1516.83dd2c1b86b4e507.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/{89849432.9eaa2f4c9c010965.js → 16e57f67.3ea19e893461e611.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/1729-d73dd25e564679d4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1505.5826671109111e75.js → 1743.563d3168e95825ef.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/1843.5924c24295243261.js +27 -0
- package/apps/pwa/out/_next/static/chunks/1987.e28149724b92918b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2066-5ca18ffa5a4be68b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2240.d10a66695155fd93.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2391.e2cbc98d4abbb41a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2528.3868070ef1be69f9.js +24 -0
- package/apps/pwa/out/_next/static/chunks/2566-734cb83bbd405886.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2697.76ca2ac3efef6f3b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/275.0e6bad414729bfa0.js +32 -0
- package/apps/pwa/out/_next/static/chunks/2803-33bdae771855a8d1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2846.d6a433b8663819a5.js +309 -0
- package/apps/pwa/out/_next/static/chunks/3143.257997a5f1ed79df.js +34 -0
- package/apps/pwa/out/_next/static/chunks/{5967-c0a80f8ffe5d4050.js → 3259-86f3be56e43eaf6a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3280-e177c33bfbdcfb0a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3294-2b6a6d45a46a6c1c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3309.364573ceedbdfd5f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3522.758840fc5e7fe630.js +287 -0
- package/apps/pwa/out/_next/static/chunks/3525-a7c989c1a661e880.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3694.b1a66387756458c0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/370.124f9e678a76fc01.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3797-9643105335e660fe.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{5578.ac963634373b28c6.js → 4084.15ecaf4240bd9e77.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/4089-b3382b8843114eee.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8438.ee7f0df3b3f6fcb4.js → 4204.a409ab6250c0f6a8.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4293.928a8d573c16aab7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4402.f9238a3b18a7bd43.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4490-9b090b0f9427f902.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4548.13502d0ca3b4f862.js +8 -0
- package/apps/pwa/out/_next/static/chunks/4565-67e4c620597965c8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7905.b230b9d06cb4ad5d.js → 4593.10f5b9bd702d4a6c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4599-548bc96cda44a5a7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4625.206574e33a9e7ae8.js +104 -0
- package/apps/pwa/out/_next/static/chunks/4862-392c9a71f0d8dfb4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5054.2906d1b02542edc5.js +149 -0
- package/apps/pwa/out/_next/static/chunks/{8569.b853a8daa8f31d12.js → 5081.a240b63fad28663a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5102-c822bbbee5a022a4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5194-8c93f2e7e4b6f717.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5305-bacd7eca0b9a5848.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5311.9071ee3f44fd7ff5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5369.9f34353442daa421.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2528.0af5c0e1fad88df6.js → 5408.377c8c543635b81f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{9502-32c5dc22e8f120f5.js → 5492-e05e26a033ba80d2.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/5785-ea8d2c633b6688e3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/589-49d730cb8138b397.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5904-264a6412ae3c9028.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5980.728531d5c566535c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/599-a914cc0468f56143.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6011.f525002e21bef0ac.js +23 -0
- package/apps/pwa/out/_next/static/chunks/6132.4381ab93683f1acf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6149.2e698635e454d8f6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{5974.360f8441eb0ac1ec.js → 6196.34d6b229079e9e28.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/6356-0afafc4230cf9767.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6406.64756073f7df35b8.js +77 -0
- package/apps/pwa/out/_next/static/chunks/6479-16e62aedafa0e310.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6599.b5246b46a2453329.js +64 -0
- package/apps/pwa/out/_next/static/chunks/{ce383d63.d2ede338788ed18e.js → 65ebff95.081daeff622d238e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/6610.5c26eec011565acb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/666.37d18474edc1c552.js +2 -0
- package/apps/pwa/out/_next/static/chunks/6904.a0b5b1ed4f2d2c20.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{83cfc5c8.349535543c1548b6.js → 6def8a8e.c81f9ca41028eccf.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7247-1f4aba94aba50d9f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7274-e71a7c5b7760cd02.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7290.1fcaeef3adb5d0d4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7477-2c64e42760f82f33.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2721.bc9a8f36bd85c07a.js → 7483.ba01c6c209b81bba.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7505-47002b76ff1aef6c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7600-11884ca4da68e5d9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7607.40d8bb3689697ccb.js +62 -0
- package/apps/pwa/out/_next/static/chunks/{6702.ebbd165d5e4a4a14.js → 7752.5cbdd0b433c54af9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7763.f51fcc19ca1414ec.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7785.846140c859b1adf6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7807-a0fdac6393e0bf36.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7837-20a63b4b7e9342b1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/800.7815414cf280545d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8925-2abfb48af477fb7f.js → 8085-1e5fce59500a2cc2.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8100.415f2105faa63e97.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8290.701aa42ed13aa767.js +226 -0
- package/apps/pwa/out/_next/static/chunks/{b4e12462-5fab4a813a63dadd.js → 83a80514-b897b64df43ce108.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8444.d9c74214a3145a43.js +93 -0
- package/apps/pwa/out/_next/static/chunks/8569.98c915534928c7f9.js +24 -0
- package/apps/pwa/out/_next/static/chunks/{8264.00e434fbd02e9311.js → 8578.dff22d988cc2b106.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/8591.b1c7f876e5ec7cfb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8715-f63756021d3ab989.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8879.776c2ec7732aa114.js +115 -0
- package/apps/pwa/out/_next/static/chunks/8881.ddca15b3c82c490c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8946.5da5c87f32a2e1a8.js +41 -0
- package/apps/pwa/out/_next/static/chunks/9117.080b9b75be90e14d.js +74 -0
- package/apps/pwa/out/_next/static/chunks/9167.f3ecb28f367d6e69.js +94 -0
- package/apps/pwa/out/_next/static/chunks/9224.66095370ee28c93b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9238-0682b013c83d6449.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7518.49161e8f5814abdb.js → 9311.24c8f5ea793253dd.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{9976.8f28864317639fe6.js → 94.4cfed21c0b781a06.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/9449.d3ed795da4befb1c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/949.1e7d45ccb71580aa.js +90 -0
- package/apps/pwa/out/_next/static/chunks/9638-d268329f2c6f4f8d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{0e3acd34.d42b2ef87fdf4234.js → 973cbf1b.3bbcf2b41548eca7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9842-d6644eb53d3dc6bf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9863.94ca3263e760696e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6571-926ddf024a6ad5df.js → 9917-9e26abc5f7772735.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9938.b23e4d4dfa0114cd.js +79 -0
- package/apps/pwa/out/_next/static/chunks/{740.90b31555290236a9.js → 9974.e77b4ff4884557b3.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/{4c5e95ce.83e9b16add6fcd8c.js → ab4d57c2.e90bc2cb31949045.js} +7 -7
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-d9babd8795f396a5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-9360b1285b2a346e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-f08f6529d09ffae1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-f000d54d258f9ef6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/{page-ad3bd8084048bee4.js → page-b12149c51f7d04b1.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-22f674e7e4c2c9d5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-8888733e40cea6d1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-9717ec6964b92276.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-44eaad486bc98a45.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-97fa6cf3d34c7eb9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-305cd294e754906e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/{page-a648bc464c9dc754.js → page-dea4b795741126fd.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-35e05d7450461a67.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-45889e7070d11126.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-16a2e893f83ae4d0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-d9babd8795f396a5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/page-b2638013c0bf7dfd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-56888bc744e08dac.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-c44f93a41167536f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-952c6ceec6d9de38.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-6d0202af568f4c63.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-1b810474507f3702.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-74366ae2693a7d95.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-4fb6fc34e20c07b7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/page-e9502921f4f01b1f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-b6169ea2f09cf287.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-6a14333d1b1b3967.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-8c91a9a66c9ec90a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/{page-48fb31810693c357.js → page-4e634c5b98fd4e93.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-ed3070d637850548.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/{page-cb154acadb025414.js → page-b9cc8a10bc6078b4.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/page-3d3fc73dd187ed52.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-128458c8d86fbb5f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-9c04dac0831c2bbb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-cf0c2b0183991d09.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-04de9ca6d52a3893.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-62527677bbc2fdc4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-9a2e09c4a311764c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-d9babd8795f396a5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-bfeb35d69e8b24bb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-89cb33bced824e4a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-b008aff96be56654.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-814394dcbae789b1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-6a87325c87f09dd2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/page-6445ac4cbb8dd869.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-852170303c6cd755.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-087ceeb975ef272b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-97c7361cc2a19319.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-20162174b77f96cf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-8c6901833f897496.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-05a4b2c9b91034aa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/dcf24383-2f82e35f2d827393.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{53746282.1be36038d1abd949.js → e117f801.e92bf10242653497.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/e87d89b9.2e71c0ae475d28a1.js +122 -0
- package/apps/pwa/out/_next/static/chunks/{0673cc54.8e2ef1c23e49e190.js → f4dfbfc0.a69fb12f854882aa.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/framework-bf135b997155e90e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-5571537543bea91c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-25c51c7df7f3a442.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-e3b2c1f5d82abd65.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-083f2e23e9bc3641.js +1 -0
- package/apps/pwa/out/_next/static/chunks/webpack-e94d22b7a028217b.js +1 -0
- package/apps/pwa/out/_next/static/css/72becfd42807b50d.css +1 -0
- package/apps/pwa/out/_next/static/{rfkMm4movqkqMebBrCXiR → dRS6lEhSRRBr1abbzp7fJ}/_buildManifest.js +1 -1
- 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 +23 -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 +23 -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/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/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/release-loop.yaml +5 -5
- package/graphs/steward/recipes.yaml +4 -0
- package/graphs/steward/steward.yaml +10 -1
- package/package.json +1 -1
- package/scripts/add-api-key.sh +1 -1
- package/scripts/bench-report.ts +46 -1
- package/scripts/ci-typecheck-changed.ts +73 -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/land-global-checks.ts +3 -0
- package/scripts/measure-log-diet-2.ts +213 -0
- package/scripts/measure-tsc-affected-roots.ts +289 -0
- package/scripts/release-loop/auto-approve-node.ts +6 -0
- package/scripts/release-loop/gate-closure.ts +286 -0
- package/scripts/release-loop/gate-executor.ts +28 -0
- package/scripts/release-loop/gate-node.ts +333 -37
- package/scripts/release-loop/gate-partial.ts +34 -6
- package/scripts/release-loop/ops-upgrade-node.ts +63 -42
- package/scripts/release-loop/publish-node.ts +6 -0
- package/scripts/release-loop/resume-release.ts +46 -13
- package/scripts/release-loop/resume.ts +110 -4
- package/scripts/release-loop/unattended-release.ts +22 -7
- package/scripts/tsc-baseline-cache.ts +172 -0
- package/scripts/usage-rollup.ts +72 -6
- package/scripts/ux-sim.ts +30 -1
- package/skills/youtube-master/scripts/main.ts +4 -1
- package/skills/youtube-master/src/obsidian.ts +9 -0
- package/src/agent/run-context.ts +10 -0
- package/src/agent-mission/driver.ts +18 -3
- package/src/agent-mission/resource-ladder.ts +8 -3
- package/src/agent-mission/review-loop.ts +8 -2
- package/src/agent-substrate/acp-implementer.ts +146 -0
- package/src/agent-substrate/pr-reviewer.ts +60 -13
- package/src/agent-substrate/self-review-cli.ts +170 -15
- 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/system-lookback-run.ts +16 -10
- package/src/bench/bench.ts +183 -0
- package/src/budget/cost-tier-advisor.ts +91 -0
- package/src/budget/fetchers/grok.ts +15 -5
- package/src/budget/llm-cost.ts +37 -5
- package/src/budget/shadow-arms.ts +77 -0
- package/src/budget/types.ts +10 -2
- package/src/budget/unified-usage.ts +256 -10
- package/src/cli/bench-cli.ts +34 -0
- package/src/cli/doctor-cli.ts +21 -0
- package/src/cli/friendly-errors.ts +113 -0
- package/src/cli/guardian-cli.ts +26 -1
- package/src/cli/image-cli.ts +101 -0
- package/src/cli/intake-cli.ts +3 -1
- package/src/cli/log-event-names.ts +4 -0
- package/src/cli/memory-cli.ts +182 -0
- package/src/cli/model-watch-cli.ts +4 -1
- package/src/cli/pr-cli.ts +62 -5
- package/src/cli/pr-land-overlap-evidence.ts +160 -12
- package/src/cli/pr-land-pod-shadow.ts +54 -0
- package/src/cli/release-cli.ts +27 -16
- package/src/cli/release-run-if-ready.ts +11 -0
- package/src/cli/resource-cli.ts +48 -0
- package/src/cli/retro-cli.ts +12 -3
- package/src/cli/rfc-status.ts +52 -2
- package/src/cli/seat-requests-cli.ts +25 -0
- package/src/cli/self-update.ts +104 -0
- package/src/cli/tasks-cli.ts +139 -10
- package/src/cli/usage-cli.ts +93 -8
- package/src/compact/verify-probe.ts +23 -3
- package/src/context-card/index.ts +50 -0
- package/src/control/control-status.ts +30 -1
- package/src/core-turn/run-goal-loop.ts +5 -4
- package/src/dashboard/ad-run-setup-bindings.ts +279 -0
- package/src/dashboard/index.ts +18 -276
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +4 -138
- package/src/dashboard/slash-runtime/shell-slash.ts +145 -0
- package/src/dashboard/stale-install-check.ts +11 -0
- package/src/debug/log-diet.ts +53 -10
- package/src/debug/log.ts +19 -1
- package/src/domains/morning-essential.ts +18 -3
- package/src/domains/release-tool.ts +43 -0
- package/src/error-report/report.ts +21 -4
- package/src/execution-loop/launch-gate.ts +158 -2
- package/src/fleet/host-doctor.ts +375 -0
- package/src/github/pr-labels.ts +1 -0
- package/src/goals/judge.ts +20 -6
- package/src/graph-runner/graph-cli.ts +42 -1
- package/src/graph-runner/runner.ts +176 -11
- package/src/graph-wizard/archetypes.ts +14 -8
- package/src/graph-wizard/generate.ts +77 -1
- package/src/grok/usage.ts +23 -2
- package/src/grok.ts +13 -7
- package/src/grounding/ref-index.ts +253 -0
- package/src/harness/dispatch-backpressure.ts +10 -2
- package/src/harness/harness-clean.ts +199 -78
- package/src/harness/harness-cli-command.ts +176 -52
- package/src/harness/harness-gc.ts +331 -0
- package/src/harness/harness-stop.ts +82 -10
- package/src/harness/harness-substrate-default.ts +142 -7
- package/src/harness/launch-registry.ts +6 -0
- package/src/harness/worktree-branch-prefix.ts +9 -0
- package/src/hq/fence.ts +187 -0
- package/src/hq/seats.ts +38 -22
- package/src/images/image-ledger.ts +179 -0
- package/src/index.ts +110 -186
- 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 +7 -2
- package/src/intake-plane/lens.ts +46 -4
- package/src/intake-plane/ref-prefetch.ts +110 -0
- package/src/intake-plane/requirement-funnel.ts +53 -1
- package/src/intelligence-map/catalog-feed.ts +119 -0
- package/src/intelligence-map/model-catalog.ts +72 -0
- package/src/intelligence-map/model-watch-intake.ts +40 -0
- package/src/intelligence-map/model-watch-mission.ts +14 -1
- package/src/llm/bedrock-eventstream.ts +215 -0
- package/src/llm/bedrock-provider.ts +114 -41
- package/src/llm/bedrock.ts +174 -28
- package/src/llm/open-weight-discipline.ts +21 -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/zai-coding-plan.ts +700 -0
- package/src/llm.ts +91 -24
- package/src/loops/checker.ts +148 -9
- package/src/loops/orchestrator/resync-shadow.ts +34 -7
- package/src/maturity/feature-maturity.ts +3 -3
- package/src/model-tier/llm-tier-map.ts +9 -5
- package/src/models/prompts.ts +2 -1
- package/src/mss/logging/log-store.ts +58 -11
- package/src/nexus/api/http-server.ts +22 -0
- package/src/nexus/api/missions.ts +73 -2
- package/src/nexus/api/ops-api.ts +18 -0
- package/src/nexus/index.ts +7 -75
- package/src/nexus/tools-kind.ts +78 -0
- package/src/oauth/codex-account-rotation.ts +43 -3
- package/src/oauth/codex-account-store.ts +339 -27
- package/src/oauth/codex-quota-policy.ts +33 -4
- package/src/oauth/fallback-chain.ts +53 -15
- package/src/onboarding/first-run.ts +37 -18
- package/src/onboarding/web-first.ts +20 -2
- 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/preview/terminal.ts +15 -5
- package/src/project/project-store.ts +20 -0
- package/src/prompt-cache/openai.ts +3 -0
- package/src/prompt-cache/types.ts +5 -0
- package/src/prompt-enhance/enhance.ts +23 -0
- package/src/release-loop/auto-start.ts +7 -5
- package/src/release-loop/checklist.ts +11 -1
- package/src/release-loop/gate-shards.ts +4 -0
- package/src/release-loop/light-rc.ts +84 -13
- package/src/release-loop/merged-pr-checklist.ts +35 -5
- package/src/release-loop/placement.ts +16 -1
- package/src/release-loop/rehearsal.ts +1 -1
- package/src/requirements/requirement-ledger.ts +536 -0
- package/src/retro/daily.ts +5 -5
- package/src/seat-loop/duty-ledger.ts +64 -0
- package/src/seat-loop/seat-loop.ts +32 -10
- package/src/self-dev/decompose.ts +37 -3
- package/src/self-dev/dev-cli.ts +44 -10
- package/src/self-dev/dev-pipeline.ts +39 -9
- package/src/self-dev/draft-residue.ts +27 -5
- package/src/self-dev/draft-sweep.ts +151 -0
- package/src/self-dev/fabric-decompose-adapter.ts +9 -2
- package/src/self-dev/graph-journey-route.ts +6 -0
- package/src/self-dev/orchestrate-cli.ts +25 -3
- package/src/self-dev/orchestrate.ts +41 -6
- package/src/self-dev/run-supervisor.ts +218 -15
- package/src/self-dev/run-types.ts +13 -0
- package/src/self-dev/supervisor-verdict-edges.ts +1 -0
- 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/flex-signals.ts +59 -0
- package/src/self-implement/frozen-merges.ts +8 -3
- package/src/self-implement/gate-cli.ts +29 -10
- package/src/self-implement/gate-remote.ts +4 -1
- package/src/self-implement/goal-author-decompose.ts +10 -2
- package/src/self-implement/goal-author.ts +36 -11
- package/src/self-implement/goal-new-paths.ts +42 -0
- package/src/self-implement/graph-overlay-condition.ts +28 -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/heal-intake.ts +5 -1
- package/src/self-implement/host-regate.ts +124 -30
- package/src/self-implement/importer-test-index.ts +3 -1
- package/src/self-implement/l8-merge-queue.ts +80 -2
- package/src/self-implement/merge-train.ts +19 -0
- package/src/self-implement/off-diff-evidence.ts +40 -1
- package/src/self-implement/orchestrator.ts +230 -32
- package/src/self-implement/premise-review.ts +159 -0
- package/src/self-implement/regate-detail.ts +79 -0
- package/src/self-implement/review-deferred-to-host.ts +21 -0
- package/src/self-implement/rework-budget-judgment.ts +23 -3
- package/src/self-implement/running-runs.ts +75 -5
- package/src/self-implement/scope-adjacency.ts +23 -0
- package/src/self-implement/seams.ts +89 -8
- package/src/self-implement/self-implement-cli.ts +9 -4
- package/src/self-implement/stop-autoheal.ts +69 -2
- package/src/session-runtime/retry-policy.ts +2 -1
- package/src/skills/tools/agent.ts +16 -1
- package/src/skills/tools/persistent-grounding.ts +11 -1
- package/src/skills/tools/shell.ts +33 -1
- package/src/steward/launch.ts +5 -4
- package/src/steward/rejudge-tick.ts +142 -0
- package/src/steward/triage-budget.ts +62 -0
- package/src/steward/triage.ts +83 -22
- package/src/task-agent/actions.ts +62 -5
- package/src/task-agent/board.ts +92 -3
- package/src/task-agent/card-evidence.ts +88 -1
- package/src/task-agent/completion-evidence.ts +5 -0
- package/src/task-agent/cover.ts +166 -3
- package/src/task-agent/judge.ts +16 -0
- package/src/task-agent/land-order-ledger.ts +95 -0
- package/src/task-agent/live-move-history.ts +87 -1
- package/src/task-agent/live-moves.ts +350 -20
- package/src/task-agent/parent-host.ts +27 -3
- package/src/task-agent/rejudge-on-head.ts +223 -24
- package/src/task-agent/review-prior-fix.ts +146 -0
- package/src/task-agent/review-result-capture.ts +3 -1
- package/src/task-agent/run-adopt.ts +136 -0
- package/src/task-agent/shadow.ts +65 -9
- package/src/task-agent/task-hand.ts +57 -7
- 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 +14 -1
- package/src/task-orchestrator/surfaces/pod-command-job.ts +14 -0
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +79 -6
- package/src/task-orchestrator/surfaces/pod-host-acp-review.ts +122 -0
- package/src/task-orchestrator/surfaces/pod-lease.ts +17 -5
- package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +11 -5
- package/src/task-orchestrator/surfaces/pod-manifest-test-normalize.ts +23 -0
- package/src/task-orchestrator/surfaces/pod-pool.ts +73 -4
- package/src/task-orchestrator/surfaces/pod-zai-child.ts +101 -0
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +432 -35
- package/src/task-orchestrator/surfaces/self-implement.ts +19 -1
- package/src/typecheck-ratchet.ts +1 -0
- package/src/user-config.ts +69 -15
- package/src/ux-sim/fault-cred-expiry.ts +100 -0
- package/src/ux-sim/fault-pod-death.ts +74 -0
- package/src/ux-sim/fault-pool-cap.ts +81 -0
- package/src/version/packed-revision.json +1 -1
- package/src/webclone/qa-table.ts +22 -0
- package/src/workflow-runtime/triggers/registry.ts +59 -0
- package/src/workflow-runtime/triggers/webhook-source.ts +35 -5
- package/apps/pwa/out/_next/static/chunks/0a09e062-34f747a17d8d8c2e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1168.a0fb3d9db76c00bd.js +0 -77
- package/apps/pwa/out/_next/static/chunks/1325-dc3f3c21eb181cb4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1357.a843c41d2cd7ec31.js +0 -34
- package/apps/pwa/out/_next/static/chunks/1430.53f49882b17607c2.js +0 -24
- package/apps/pwa/out/_next/static/chunks/1459-be6509b3419d8a9f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/153.0a4e3cd516930491.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1572.7bc291352e241a45.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1593-d07ad068c120dfb0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1608.b7c900baa56cab32.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1615-4d8156febaac4b35.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1646.e03f0fbc26bc72f6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1669-da843ed73aa301f5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/176.13ba0659e3e96fd4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1990.9c60f495ef33c070.js +0 -36
- package/apps/pwa/out/_next/static/chunks/2042e469.f86158dd5c1e51a1.js +0 -122
- package/apps/pwa/out/_next/static/chunks/2543-28ec6aeb80a5e1e1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2561.43c99d6ee914ee6a.js +0 -62
- package/apps/pwa/out/_next/static/chunks/2638.b39ab2f674caa1a5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/272-b23d2e92161d11fd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2778.67e0c1d484189ccf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2841.10ff309f6a3168af.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2886-9639f97d523d752a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2978.57fb87ce21cc5a85.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3664.15f968c39d15bd28.js +0 -149
- package/apps/pwa/out/_next/static/chunks/3786-935eb3fcde90e496.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3813.df4a01ae89c6f63f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3961-fbef064c213b2b18.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3992.f79c70480e8c4077.js +0 -309
- package/apps/pwa/out/_next/static/chunks/40-413e8641de9ed1e5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4054-521f11a503daed64.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4315.e7356f34c7d8ec79.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4489-bf629de6f04f7681.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4540.557733cc88c6a82b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/462.0275b4338c5b6337.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4658-7ed9266098a787e4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4945-0f4046cfc3228ea3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5105-18b4165c5d3a86b0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5205-6992210fe31f45f4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5287-72898b126d722457.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5313.1697309c532b9856.js +0 -2
- package/apps/pwa/out/_next/static/chunks/5474.991b90688c3cb321.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5544-871146192d095594.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5935.d5e8ca3e096772e8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6006.84e8b418a7a0db97.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6013-1267ba7454d6764b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6038-1e0f7ee1086da4cf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/617.709dd0019fe99796.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6261.fb9bf94947094636.js +0 -74
- package/apps/pwa/out/_next/static/chunks/6411.1966866fae31ffa9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6481-b829d99aa8d158f5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6541.2b7daa043100d0fa.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6597.2d7a6d77ab485b27.js +0 -64
- package/apps/pwa/out/_next/static/chunks/67.5dd2dc543c2fa3de.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6777-312496b0d774719b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/681.0116f7036cfb4e8d.js +0 -32
- package/apps/pwa/out/_next/static/chunks/6871.30b2bf23b3126e36.js +0 -24
- package/apps/pwa/out/_next/static/chunks/6882.737d366d54e29d83.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7035.54c2c535164b1c9b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7086-8bfa7e8f796a03d9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7126.5e5599f251726b67.js +0 -93
- package/apps/pwa/out/_next/static/chunks/7203-47ff0740a293e284.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7268.017f49dafb5f0ff3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7355.5d65a87e9567ae08.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7392.4018c2a62177f296.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7471.3a3d1be50559cfb8.js +0 -148
- package/apps/pwa/out/_next/static/chunks/7500.73489a345cef2201.js +0 -226
- package/apps/pwa/out/_next/static/chunks/7565.c2f72a15c1ed0396.js +0 -27
- package/apps/pwa/out/_next/static/chunks/7651.6d1c984d3d8587a4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7713.7ab863ed59b2d661.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7793.d7cd2026767ddd54.js +0 -115
- package/apps/pwa/out/_next/static/chunks/7837.c7c267e5dceadb11.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7891.09266382daf70d1c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7990-cbf6a01bcbdcb7e6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8119-819c549e0c197786.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8186-74ea19a3700d04a8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8267-e7b3262b735ae106.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8386.86518e55b75f7cf5.js +0 -8
- package/apps/pwa/out/_next/static/chunks/8437.ba6eeeb29590042f.js +0 -23
- package/apps/pwa/out/_next/static/chunks/8469-1500be57e582296f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8483-7164cecdaf178ca6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8722-4c940f02c496ad36.js +0 -1
- package/apps/pwa/out/_next/static/chunks/887-41d0dcf5af48c628.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9039.da86361496797522.js +0 -90
- package/apps/pwa/out/_next/static/chunks/905.d5745a25884754d4.js +0 -94
- package/apps/pwa/out/_next/static/chunks/9089-d909f3d4478b0628.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9096.dc5c7a267f83790c.js +0 -41
- package/apps/pwa/out/_next/static/chunks/93.a567257b6bdb7fe4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9320.419acb9665b46597.js +0 -287
- package/apps/pwa/out/_next/static/chunks/9356.b8ceda6634612ff8.js +0 -79
- package/apps/pwa/out/_next/static/chunks/94.e9e1043b35cd3eed.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9400-7498ea90b2e6ee8f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9930-58644af9c813d6e1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9932.367cf4725fedd6e2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9935.1628b904cdc8ce0e.js +0 -104
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-a2aa8709d193ee0d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-d5341ac96203a983.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-0160ab83c8685a12.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-55c631c032dcc47e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-e6784759a2531031.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-5c5d0e370f495ef6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-c325e21543cd48a8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-6168a7c43e6ef8f5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-31417d3604154a6d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-e9606dc0bdec9305.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-25bb425afdb27541.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-32d72fdef1afe11a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-d27b794c8ba76bbb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-a2aa8709d193ee0d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-2bd9ce9c020d02d4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-a34096245e63b9f2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-22170f4f00a15496.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-4cc66d22ca4ec502.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-83b14c729fcea837.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-6e2781c523dad761.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-26dd415c8f13f341.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-ccdb83ed40c8d724.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-7ad3de8fa06fdea9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-015c02c4fec4023e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-6b0ae5b5c42f6d2f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-786111f30b6f0969.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-4e17cce5e2ed0fee.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-16258e956ea93fc2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-c2dd2b19c93f90ab.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-85f35dd1dd96ae87.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-d3c007c8a0ddd7ee.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-7837ce2aacb97b04.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-7350ee93f4fe1033.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-00723458575f2b19.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-a2aa8709d193ee0d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-745d9b6c015076f8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-736d33c7414a8a1a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-119be9a0444d2482.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-fa806c018833e332.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-c782041b5f5199ba.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-d431fbdea6ad93dd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-0c056abff82b9891.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-242d668e4fbf72c4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-847c3de6ca8a4416.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-81dd0d8ae923ed72.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-74e69ba2c8e970d4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-70038f41ae7cf931.js +0 -1
- package/apps/pwa/out/_next/static/chunks/cd39fa05.fac4545f42a3ef70.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-b2763b10dc6b3836.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-27893e93b0ec193d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-f280047194e1daf8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-e90eff2d0b70564c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-70f2d654b3435d34.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-4794f053bd5e4230.js +0 -1
- package/apps/pwa/out/_next/static/css/5619d59e87eddf66.css +0 -1
- /package/apps/pwa/out/_next/static/{rfkMm4movqkqMebBrCXiR → dRS6lEhSRRBr1abbzp7fJ}/_ssgManifest.js +0 -0
|
@@ -19,6 +19,10 @@ export interface LaunchBinding {
|
|
|
19
19
|
readonly goalPath?: string;
|
|
20
20
|
/** The `self-dev-runs` directory that holds this run's `pid.json` — tells a stop which universe launched it. */
|
|
21
21
|
readonly runsDir?: string;
|
|
22
|
+
/** `ELANOUS_POD_NAME` at launch — a Pod child's run (TA backfill skips it like the launch-time hook does). */
|
|
23
|
+
readonly podName?: string;
|
|
24
|
+
/** `ELANOUS_TASK_CARD_ID` at launch — a handed run that already owns a card (TA backfill skips it). */
|
|
25
|
+
readonly taskCardId?: string;
|
|
22
26
|
}
|
|
23
27
|
|
|
24
28
|
const RUN_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
@@ -72,6 +76,8 @@ export function readLaunchBinding(runId: string, dir = launchRegistryDir()): Lau
|
|
|
72
76
|
...(typeof value.argv0 === 'string' ? { argv0: value.argv0 } : {}),
|
|
73
77
|
...(typeof value.goalPath === 'string' && isAbsolute(value.goalPath) ? { goalPath: value.goalPath } : {}),
|
|
74
78
|
...(typeof value.runsDir === 'string' && isAbsolute(value.runsDir) ? { runsDir: value.runsDir } : {}),
|
|
79
|
+
...(typeof value.podName === 'string' && value.podName ? { podName: value.podName } : {}),
|
|
80
|
+
...(typeof value.taskCardId === 'string' && value.taskCardId ? { taskCardId: value.taskCardId } : {}),
|
|
75
81
|
};
|
|
76
82
|
} catch { return null; }
|
|
77
83
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
|
+
import { branchGoalId } from '../cli/pr-lineage.js';
|
|
2
3
|
|
|
3
4
|
/** Prefix for disposable worktree branches created by self-implement and inspected by harness clean.
|
|
4
5
|
* A successfully merged run cleans up its own worktree.
|
|
@@ -31,3 +32,11 @@ export function plannedSelfImplBranch(feature: string, goalId?: string, runId?:
|
|
|
31
32
|
const digest = createHash('sha256').update(id).digest('hex').slice(0, 8);
|
|
32
33
|
return `${WORKTREE_BRANCH_PREFIX}goalid-${id}-${readable}-${digest}${runSuffix}`;
|
|
33
34
|
}
|
|
35
|
+
|
|
36
|
+
/** 같은 골의 재실행이 공유하는 «안정한» 계보 키. `plannedSelfImplBranch` 가 싣는 `goalid-<id>-` 구간만 읽는다 —
|
|
37
|
+
* feature 문구·다이제스트·런 접미(`-r<runId>`)는 재실행마다 달라지므로 키에 넣지 않는다.
|
|
38
|
+
* ⛔ 골 식별자가 없는 옛 브랜치는 `null` — 읽기용 슬러그는 서로 다른 골이 같은 값을 낼 수 있어(`grounded-N-…`) 키가 될 수 없다. */
|
|
39
|
+
export function branchLineageKey(branch: string): string | null {
|
|
40
|
+
const id = branchGoalId(branch);
|
|
41
|
+
return id ? `goalid:${id.toLowerCase()}` : null;
|
|
42
|
+
}
|
package/src/hq/fence.ts
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { readFileSync, readlinkSync, readdirSync } from 'node:fs';
|
|
3
|
+
import { cronMatches } from '../domains/cron-match.js';
|
|
4
|
+
import { resolveTimeZone } from '../time/format.js';
|
|
5
|
+
|
|
6
|
+
export interface CliProcess { pid: number; ppid: number; ageSeconds: number; cpuSeconds: number; command: string }
|
|
7
|
+
export interface OrphanCandidate { process: CliProcess; tree: CliProcess[]; periodMinutes: number }
|
|
8
|
+
export interface FenceGcDeps {
|
|
9
|
+
snapshot?: () => CliProcess[];
|
|
10
|
+
run?: (binary: string, args: string[]) => string;
|
|
11
|
+
sleep?: (ms: number) => Promise<void>;
|
|
12
|
+
kill?: (pid: number) => void;
|
|
13
|
+
timeZone?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function cliFingerprint(command: string): string {
|
|
17
|
+
return command.replace(/^\s*(?:\S*\/)?bun\s+(?:\S*\/)?elanous\.mjs\s+/, 'elanous ')
|
|
18
|
+
.replace(/^\s*(?:\S*\/)?(?:elanous\.mjs|eln)\s+/, 'elanous ')
|
|
19
|
+
.trim();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function seconds(text: string): number {
|
|
23
|
+
const parts = text.split(':').map(Number);
|
|
24
|
+
return parts.reduce((total, part) => total * 60 + part, 0);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** ps gives command after the four numeric fields; never split command on whitespace. */
|
|
28
|
+
export function processSnapshot(): CliProcess[] {
|
|
29
|
+
const result = spawnSync('ps', ['-axo', 'pid=,ppid=,etime=,time=,command='], { encoding: 'utf8', timeout: 10_000, maxBuffer: 4 * 1024 * 1024 });
|
|
30
|
+
if (result.status !== 0) throw new Error(`ps failed: ${result.stderr}`);
|
|
31
|
+
return result.stdout.split('\n').flatMap(line => {
|
|
32
|
+
const fields = /^\s*(\d+)\s+(\d+)\s+([\d:-]+)\s+([\d:.]+)\s+(.+)$/.exec(line);
|
|
33
|
+
if (!fields) return [];
|
|
34
|
+
const elapsed = fields[3]!.split('-');
|
|
35
|
+
const ageSeconds = seconds(elapsed.at(-1)!) + (elapsed.length === 2 ? Number(elapsed[0]) * 86_400 : 0);
|
|
36
|
+
const cpuSeconds = seconds(fields[4]!);
|
|
37
|
+
if (!Number.isFinite(ageSeconds) || !Number.isFinite(cpuSeconds)) return [];
|
|
38
|
+
return [{ pid: Number(fields[1]), ppid: Number(fields[2]), ageSeconds, cpuSeconds, command: fields[5]! }];
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Only installed elanous one-shot CLI launches, not shells, daemons, or an incidental flag inside a quoted payload. */
|
|
43
|
+
export function oneShotCommand(command: string): boolean {
|
|
44
|
+
const invocation = /^(?:(?:\S*\/)?bun\s+(?:\S*\/)?elanous\.mjs|(?:\S*\/)?(?:elanous|elanous\.mjs|eln))\s+/.exec(command);
|
|
45
|
+
if (!invocation) return false;
|
|
46
|
+
const args = command.slice(invocation[0].length).split(/\s+--\s+|\s+-c\s+/)[0] ?? '';
|
|
47
|
+
const payload = /\s+--\s+(.+)$/.exec(command)?.[1] ?? '';
|
|
48
|
+
const shellBody = /\s+--\s+(?:\S*\/)?(?:sh|bash)\s+-c\s+(.+)$/.exec(command)?.[1];
|
|
49
|
+
if (shellBody !== undefined) {
|
|
50
|
+
if (!/^['"]?(?:\S*\/)?bun\s+(?:\S*\/)?elanous\.mjs\s+/.test(shellBody)
|
|
51
|
+
|| /[;|`&]/.test(shellBody) || /\bnexus\s+run\b/.test(shellBody)
|
|
52
|
+
|| !/(?:^|\s)(?:--once|tick|--refresh|--notify)(?:\s|['"]|$)/.test(shellBody)) return false;
|
|
53
|
+
}
|
|
54
|
+
// Test-universe and help invocations are never collected — on the fence itself, in its payload, or in a wrapped shell body.
|
|
55
|
+
for (const part of [args, payload, shellBody ?? '']) {
|
|
56
|
+
if (/(?:^|\s|['"])--test(?:\s|=|['"]|$)/.test(part) || /(?:^|\s)(?:--help|-h)(?:\s|['"]|$)/.test(part)) return false;
|
|
57
|
+
}
|
|
58
|
+
// The daemon is never a one-shot, whatever flag rides along (`nexus run --once`), directly or as a fence payload.
|
|
59
|
+
if (/(?:^|\s)nexus\s+run\b/.test(args) || /(?:^|\s)nexus\s+run\b/.test(payload) || /&&|\|\||[;|`&]/.test(payload)) return false;
|
|
60
|
+
if (/^hq\s+fence\s+--role\s+(?:cron|seat-loop|conatus|release-run)\b/.test(args) && payload) {
|
|
61
|
+
return /(?:^|\s)(?:--once|tick|--refresh|--notify)(?:\s|['"]|$)/.test(payload);
|
|
62
|
+
}
|
|
63
|
+
return /(?:^|\s)hq\s+fence\s+--role\s+(?:cron|seat-loop|conatus|release-run)\b/.test(args)
|
|
64
|
+
|| /(?:^|\s)--once(?:\s|$)/.test(args)
|
|
65
|
+
|| /(?:^|\s)(?:tick|--refresh|--notify)(?:\s|$)/.test(args);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Unknown/mismatched schedules are not assigned a made-up period. Match the launched action (including fence payload). */
|
|
69
|
+
export function scheduledPeriodMinutes(command: string, schedules: readonly { command: string; cron: string }[], now: Date, timeZone = resolveTimeZone().timeZone): number | null {
|
|
70
|
+
const current = cliFingerprint(command);
|
|
71
|
+
const matches = schedules.filter(job => {
|
|
72
|
+
const action = job.command.trim().replace(/^cd\s+\S+\s*&&\s*/, '').replace(/\s+>>?\s+\S+(?:\s+2>&1)?\s*$/, '');
|
|
73
|
+
if (action.length < 16) return false;
|
|
74
|
+
if (current === cliFingerprint(action)) return true;
|
|
75
|
+
// The installed hq-fence shell wrapper execs the CLI with an explicit role and -- payload.
|
|
76
|
+
// Accept only a single, unambiguous quoted command; never infer a period from a substring.
|
|
77
|
+
const wrapper = /^(?:\S*\/)?hq-fence\s+(cron|seat-loop|conatus|release-run)\s+'([^']+)'$/.exec(action);
|
|
78
|
+
if (!wrapper) return false;
|
|
79
|
+
return current === `elanous hq fence --role ${wrapper[1]} -- ${wrapper[2]}`;
|
|
80
|
+
});
|
|
81
|
+
if (matches.length !== 1) return null;
|
|
82
|
+
const cron = matches[0]!.cron;
|
|
83
|
+
if (cron.trim().split(/\s+/).length !== 5) return null;
|
|
84
|
+
const start = Math.floor(now.getTime() / 60_000) * 60_000;
|
|
85
|
+
// A 7-day minute scan per process is costly with many hung trees; one tick asks the same (cron, zone, minute) often.
|
|
86
|
+
const memoKey = `${cron}\0${timeZone}\0${start}`;
|
|
87
|
+
if (periodMemo.has(memoKey)) return periodMemo.get(memoKey)!;
|
|
88
|
+
const period = cronPeriodMinutes(cron, start, timeZone);
|
|
89
|
+
if (periodMemo.size > 256) periodMemo.clear();
|
|
90
|
+
periodMemo.set(memoKey, period);
|
|
91
|
+
return period;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const periodMemo = new Map<string, number | null>();
|
|
95
|
+
|
|
96
|
+
function cronPeriodMinutes(cron: string, start: number, timeZone: string): number | null {
|
|
97
|
+
const fires: number[] = [];
|
|
98
|
+
try {
|
|
99
|
+
// Every fire in the next 7 days, not just the first few: a weekday-hours cron (`0 9-17 * * 1-5`) is hourly
|
|
100
|
+
// for three gaps and then pauses for a night or a weekend — that is no single period.
|
|
101
|
+
for (let i = 0; i < 7 * 24 * 60; i++) {
|
|
102
|
+
const at = start + i * 60_000;
|
|
103
|
+
if (cronMatches(cron, new Date(at), { timeZone })) fires.push(at);
|
|
104
|
+
}
|
|
105
|
+
} catch { return null; }
|
|
106
|
+
// An irregular schedule (any unequal gap within the window, e.g. `0 9,18 * * *`, weekday hours) has no single period
|
|
107
|
+
// to multiply — unknown, never collected. Fewer than three fires in 7 days (weekly) is unknown too (safe side).
|
|
108
|
+
if (fires.length < 3) return null;
|
|
109
|
+
const gap = fires[1]! - fires[0]!;
|
|
110
|
+
for (let i = 2; i < fires.length; i++) if (fires[i]! - fires[i - 1]! !== gap) return null;
|
|
111
|
+
return gap / 60_000;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function orphanCandidates(first: readonly CliProcess[], second: readonly CliProcess[], schedules: readonly { command: string; cron: string }[], now: Date, timeZone?: string): OrphanCandidate[] {
|
|
115
|
+
const byPid = new Map(second.map(row => [row.pid, row]));
|
|
116
|
+
const initial = new Map(first.map(row => [row.pid, row]));
|
|
117
|
+
const candidates: OrphanCandidate[] = [];
|
|
118
|
+
// The collector itself and every ancestor of it (the guardian tick's cron/fence parents) are never signalled.
|
|
119
|
+
const self = new Set<number>([process.pid]);
|
|
120
|
+
for (let pid: number | undefined = byPid.get(process.pid)?.ppid; pid !== undefined && pid > 1 && !self.has(pid); pid = byPid.get(pid)?.ppid) self.add(pid);
|
|
121
|
+
for (const root of first) {
|
|
122
|
+
if (root.pid === process.pid || !oneShotCommand(root.command)) continue;
|
|
123
|
+
const current = byPid.get(root.pid);
|
|
124
|
+
if (!current || current.command !== root.command || current.ppid !== root.ppid || current.cpuSeconds !== root.cpuSeconds) continue;
|
|
125
|
+
// Identity: a reused PID restarts its age; the same process can only have grown older between snapshots.
|
|
126
|
+
if (current.ageSeconds < root.ageSeconds) continue;
|
|
127
|
+
const periodMinutes = scheduledPeriodMinutes(root.command, schedules, now, timeZone);
|
|
128
|
+
if (!periodMinutes || root.ageSeconds <= periodMinutes * 3 * 60) continue;
|
|
129
|
+
// An old ancestor with an actively executing payload is not an orphan.
|
|
130
|
+
if (second.some(row => row.ppid === root.pid && !initial.has(row.pid))) continue;
|
|
131
|
+
const tree = [current];
|
|
132
|
+
for (let i = 0; i < tree.length; i++) {
|
|
133
|
+
for (const child of second) if (child.ppid === tree[i]!.pid && !tree.some(row => row.pid === child.pid)) tree.push(child);
|
|
134
|
+
}
|
|
135
|
+
if (tree.some(row => {
|
|
136
|
+
const before = initial.get(row.pid);
|
|
137
|
+
return !before || before.command !== row.command || before.cpuSeconds !== row.cpuSeconds || before.ppid !== row.ppid
|
|
138
|
+
|| Math.abs((row.ageSeconds - before.ageSeconds) - (current.ageSeconds - root.ageSeconds)) > 2;
|
|
139
|
+
})) continue;
|
|
140
|
+
if (tree.some(row => self.has(row.pid))) continue;
|
|
141
|
+
candidates.push({ process: current, tree, periodMinutes });
|
|
142
|
+
}
|
|
143
|
+
return candidates.filter(candidate => !candidates.some(parent => parent !== candidate && parent.tree.some(row => row.pid === candidate.process.pid)));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function gcTools(deps: FenceGcDeps = {}) {
|
|
147
|
+
return {
|
|
148
|
+
snapshot: deps.snapshot ?? processSnapshot,
|
|
149
|
+
sleep: deps.sleep ?? ((ms: number) => new Promise<void>(resolve => setTimeout(resolve, ms))),
|
|
150
|
+
run: deps.run ?? ((binary: string, args: string[]) => {
|
|
151
|
+
// Linux has no macOS sample. Over one second, sample each thread's wait channel and state (readable by the
|
|
152
|
+
// process owner); the kernel stack is root-only on most hosts, so it is added only when readable.
|
|
153
|
+
if (binary === 'sample' && process.platform === 'linux') {
|
|
154
|
+
const pid = args[0];
|
|
155
|
+
const samples: string[] = [];
|
|
156
|
+
const deadline = Date.now() + 1_000;
|
|
157
|
+
do {
|
|
158
|
+
for (const tid of readdirSync(`/proc/${pid}/task`)) {
|
|
159
|
+
const base = `/proc/${pid}/task/${tid}`;
|
|
160
|
+
const state = /^State:\s*(.+)$/m.exec(readFileSync(`${base}/status`, 'utf8'))?.[1] ?? '?';
|
|
161
|
+
let stack = '';
|
|
162
|
+
// Root-only on most kernels: carried when readable, otherwise the evidence says so (not a collection gate).
|
|
163
|
+
try { stack = `\n${readFileSync(`${base}/stack`, 'utf8')}`; } catch (error) { stack = ` kernel-stack=unreadable(${(error as NodeJS.ErrnoException).code ?? 'error'})`; }
|
|
164
|
+
samples.push(`tid=${tid} state=${state} wchan=${readFileSync(`${base}/wchan`, 'utf8') || '0'}${stack}`);
|
|
165
|
+
}
|
|
166
|
+
if (Date.now() < deadline) Bun.sleepSync(Math.min(100, deadline - Date.now()));
|
|
167
|
+
} while (Date.now() < deadline);
|
|
168
|
+
if (samples.length === 0) throw new Error(`sample: no threads for PID ${pid}`);
|
|
169
|
+
return samples.join('\n');
|
|
170
|
+
}
|
|
171
|
+
// macOS `sample` by absolute path: a PATH shim (e.g. pyenv's `sample`) must not stand in for the profiler.
|
|
172
|
+
const resolved = binary === 'sample' && process.platform === 'darwin' ? '/usr/bin/sample' : binary;
|
|
173
|
+
// sample symbolicates a large Bun binary after its 1s window — allow time and room, or it would never succeed.
|
|
174
|
+
const result = spawnSync(resolved, args, { encoding: 'utf8', timeout: binary === 'sample' ? 30_000 : 10_000, maxBuffer: 8 * 1024 * 1024 });
|
|
175
|
+
if (result.status !== 0) throw new Error(`${binary}: ${result.error ?? result.stderr ?? 'failed'}`);
|
|
176
|
+
return result.stdout;
|
|
177
|
+
}),
|
|
178
|
+
kill: deps.kill ?? ((pid: number) => process.kill(pid, 'SIGTERM')),
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** The target's own Bun binary — read from the target PID, so a PID that died (or was reused) fails closed. */
|
|
183
|
+
export function bunVersion(pid: number, run: (binary: string, args: string[]) => string): string {
|
|
184
|
+
const bin = process.platform === 'linux' ? readlinkSync(`/proc/${pid}/exe`) : run('ps', ['-o', 'comm=', '-p', String(pid)]).trim();
|
|
185
|
+
if (!/(?:^|\/)bun(?:$|\s)/.test(bin)) throw new Error(`PID ${pid} is not a Bun process`);
|
|
186
|
+
return run(bin, ['--version']).trim();
|
|
187
|
+
}
|
package/src/hq/seats.ts
CHANGED
|
@@ -47,6 +47,12 @@ export function resolveHqLaunchSeat(role: string, home: string, options: { root?
|
|
|
47
47
|
if (head.status !== 1) throw new Error(`git symbolic-ref --quiet HEAD: ${head.stderr.trim() || `exit ${head.status}`}`);
|
|
48
48
|
return path;
|
|
49
49
|
}
|
|
50
|
+
|
|
51
|
+
/** Installed-CLI argv: absolute runtime + absolute CLI entry, so a launch never resolves `bin/elanous.mjs` against its cwd. */
|
|
52
|
+
export function installedCliArgv(args: readonly string[] = []): string[] {
|
|
53
|
+
return [process.execPath, resolve(import.meta.dir, '../../bin/elanous.mjs'), ...args];
|
|
54
|
+
}
|
|
55
|
+
|
|
50
56
|
function readLedger(root: string): HouseLedger {
|
|
51
57
|
const ledger = JSON.parse(readFileSync(ledgerPath(root), 'utf8')) as HouseLedger;
|
|
52
58
|
if (!ledger.home?.path || !ledger.sandboxes || typeof ledger.sandboxes !== 'object') throw new Error('invalid house.json; refusing to guess houses');
|
|
@@ -157,7 +163,7 @@ export function proveMergedPr(repo: string, branch: string, tip: string, gh: (ar
|
|
|
157
163
|
: { status: parsed.length >= MERGED_PR_QUERY_LIMIT ? 'unknown' : 'not-merged' };
|
|
158
164
|
}
|
|
159
165
|
|
|
160
|
-
//
|
|
166
|
+
// Attached tips use their branch; a detached HEAD is checked separately from the registered work tip.
|
|
161
167
|
function landedTip(repo: string, branch: string, tip: string, gh: (args: string[]) => GhCliResult): 'ancestor' | 'pr-merged' | 'tree-equal' | null {
|
|
162
168
|
if (mergedIntoMain(repo, tip)) return 'ancestor';
|
|
163
169
|
if (proveMergedPr(repo, branch, tip, gh, 'main').status === 'pr-merged') return 'pr-merged';
|
|
@@ -170,8 +176,8 @@ function judgeLandedTip(repo: string, branch: string, tip: string, gh: (args: st
|
|
|
170
176
|
return rule !== null;
|
|
171
177
|
}
|
|
172
178
|
|
|
173
|
-
// Squash changes
|
|
174
|
-
//
|
|
179
|
+
// Squash changes commit identity. Check the changed paths together against a fetched main
|
|
180
|
+
// snapshot, not merely the latest tree: a later main commit may have edited those paths again.
|
|
175
181
|
function treeEqualToMain(repo: string, tip: string): boolean {
|
|
176
182
|
const main = 'refs/remotes/origin/main';
|
|
177
183
|
const base = git(repo, 'merge-base', tip, main);
|
|
@@ -179,9 +185,15 @@ function treeEqualToMain(repo: string, tip: string): boolean {
|
|
|
179
185
|
if (changed.status !== 0) throw new Error(`cannot verify changed files: ${changed.stderr.trim() || `exit ${changed.status}`}`);
|
|
180
186
|
const files = changed.stdout.split('\0').filter(Boolean);
|
|
181
187
|
if (files.length === 0) return false;
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
|
|
188
|
+
const equalAt = (commit: string): boolean => {
|
|
189
|
+
const equal = runGitCommand(repo, ['diff', '--quiet', tip, commit, '--', ...files], { encoding: 'utf8' });
|
|
190
|
+
if (equal.status !== 0 && equal.status !== 1) throw new Error(`cannot verify tree equality: ${equal.stderr.trim() || `exit ${equal.status}`}`);
|
|
191
|
+
return equal.status === 0;
|
|
192
|
+
};
|
|
193
|
+
if (equalAt(main)) return true;
|
|
194
|
+
// Only first-parent main snapshots can establish a previous landing; a side branch alone cannot.
|
|
195
|
+
const history = git(repo, 'log', '--first-parent', '--format=%H', `${base}..${main}`, '--', ...files);
|
|
196
|
+
return history.split('\n').filter(Boolean).some(equalAt);
|
|
185
197
|
}
|
|
186
198
|
// Git lock files are created exclusively; ours carry our pid. A lock left by a killed `work done` is never
|
|
187
199
|
// removed automatically (between our check and a delete, a live git could have re-taken it): the error names it.
|
|
@@ -398,49 +410,53 @@ export function hqWork(action: 'new' | 'done', value: string, nameValue: string,
|
|
|
398
410
|
// Resume: an interrupted done left the work detached at its recorded merged tip, with the branch either
|
|
399
411
|
// still at that tip (stopped before the delete) or already deleted (stopped before the removal).
|
|
400
412
|
const branchPresent = branchTip.status === 0;
|
|
401
|
-
const
|
|
402
|
-
|
|
413
|
+
const head = git(path, 'rev-parse', 'HEAD');
|
|
414
|
+
const resuming = attached.status === 1 && entry.reclaiming !== undefined
|
|
415
|
+
&& head === (entry.checkoutTip ?? entry.reclaiming)
|
|
403
416
|
&& (!branchPresent || branchTip.stdout.trim() === entry.reclaiming);
|
|
404
|
-
if (!resuming && (attached.status !== 0 || !branchPresent)) throw new Error(`work branch mismatch: ${path}`);
|
|
417
|
+
if (!resuming && (attached.status !== 0 && attached.status !== 1 || !branchPresent)) throw new Error(`work branch mismatch: ${path}`);
|
|
405
418
|
const ownTip = resuming ? entry.reclaiming! : branchTip.stdout.trim();
|
|
406
|
-
const
|
|
419
|
+
const detached = !resuming && attached.status === 1;
|
|
420
|
+
const detachedCheckout = detached || (resuming && entry.checkoutBranch === branch && entry.checkoutTip !== undefined && entry.checkoutTip !== ownTip);
|
|
421
|
+
const checkoutBranch = resuming ? entry.checkoutBranch ?? branch : detached ? branch : attached.stdout.trim();
|
|
407
422
|
const checkoutRef = `refs/heads/${checkoutBranch}`;
|
|
408
423
|
const differentBranch = checkoutBranch !== branch;
|
|
409
|
-
const tip = differentBranch
|
|
410
|
-
?
|
|
411
|
-
|
|
412
|
-
if (!resuming && differentBranch && git(path, 'rev-parse', 'HEAD') !== tip) throw new Error(`work branch moved before the merge check: ${checkoutBranch}`);
|
|
424
|
+
const tip = detached ? head : resuming && entry.checkoutTip ? entry.checkoutTip : differentBranch
|
|
425
|
+
? git(repo, 'rev-parse', '--verify', checkoutRef) : ownTip;
|
|
426
|
+
if (!resuming && !detached && differentBranch && head !== tip) throw new Error(`work branch moved before the merge check: ${checkoutBranch}`);
|
|
413
427
|
// The ancestor check is against origin/main, not bare HEAD (which may still point to master).
|
|
414
428
|
if (!judgeLandedTip(repo, checkoutBranch, tip, options.gh ?? runGhCliWithResult)) throw new Error(`work is not merged into origin/main: ${checkoutBranch}`);
|
|
415
|
-
if (differentBranch && !judgeLandedTip(repo, branch, ownTip, options.gh ?? runGhCliWithResult)) throw new Error(`work is not merged into origin/main: ${branch}`);
|
|
429
|
+
if ((differentBranch || detachedCheckout) && tip !== ownTip && !judgeLandedTip(repo, branch, ownTip, options.gh ?? runGhCliWithResult)) throw new Error(`work is not merged into origin/main: ${branch}`);
|
|
416
430
|
if (differentBranch && !resuming) {
|
|
417
431
|
const current = runGitCommand(repo, ['rev-parse', '--verify', checkoutRef], { encoding: 'utf8' });
|
|
418
432
|
if (current.status !== 0 || current.stdout.trim() !== tip || git(path, 'rev-parse', 'HEAD') !== tip) throw new Error(`work branch moved before removal: ${checkoutBranch}`);
|
|
419
433
|
}
|
|
420
434
|
// Removal order keeps every interruption resumable: record the proven checkout tip and original branch tip
|
|
421
435
|
// → detach → hold the work's git locks → compare-and-delete the original branch → remove the worktree
|
|
422
|
-
// → drop the registration last. A different attached branch is proven but its ref is never deleted
|
|
436
|
+
// → drop the registration last. A different attached branch is proven but its ref is never deleted;
|
|
437
|
+
// a pre-existing detached HEAD is checked independently and never reattached on a failed removal.
|
|
423
438
|
// Holding this worktree's HEAD.lock and index.lock makes git refuse every commit and `add` in it until the
|
|
424
439
|
// removal ends (`worktree remove` itself does not need them), so no commit can land between the last
|
|
425
440
|
// check and the removal. The locks live in the worktree's admin dir, which the removal deletes.
|
|
426
441
|
updateLedgerLocked(root, ledger => { ledger.sandboxes[path] = { ...entry, reclaiming: ownTip,
|
|
427
|
-
...(differentBranch ? { checkoutTip: tip, checkoutBranch } : {}) }; });
|
|
442
|
+
...(differentBranch || (detachedCheckout && tip !== ownTip) ? { checkoutTip: tip, checkoutBranch } : {}) }; });
|
|
428
443
|
const admin = git(path, 'rev-parse', '--absolute-git-dir');
|
|
429
444
|
const held: string[] = [];
|
|
430
445
|
const release = (): void => { for (const lock of held.splice(0)) rmSync(lock, { force: true }); };
|
|
431
446
|
const restore = (): string => {
|
|
432
447
|
release();
|
|
433
448
|
const head = runGitCommand(path, ['rev-parse', '--verify', 'HEAD'], { encoding: 'utf8' });
|
|
434
|
-
runGitCommand(repo, ['update-ref', ref, differentBranch ? ownTip : head.status === 0 ? head.stdout.trim() : tip, ''], { encoding: 'utf8' });
|
|
435
|
-
const reattached =
|
|
436
|
-
|
|
449
|
+
runGitCommand(repo, ['update-ref', ref, differentBranch || detachedCheckout ? ownTip : head.status === 0 ? head.stdout.trim() : tip, ''], { encoding: 'utf8' });
|
|
450
|
+
const reattached = detachedCheckout ? head.status === 0 && head.stdout.trim() === tip
|
|
451
|
+
: runGitCommand(path, ['symbolic-ref', 'HEAD', checkoutRef], { encoding: 'utf8' }).status === 0;
|
|
452
|
+
// If HEAD could not be restored (e.g. a stale HEAD.lock), keep `reclaiming` so the next done resumes.
|
|
437
453
|
const restored = reattached ? { kind: entry.kind, path: entry.path, owner: entry.owner } : { ...entry, reclaiming: ownTip,
|
|
438
|
-
...(differentBranch ? { checkoutTip: tip, checkoutBranch } : {}) };
|
|
454
|
+
...(differentBranch || (detachedCheckout && tip !== ownTip) ? { checkoutTip: tip, checkoutBranch } : {}) };
|
|
439
455
|
try { updateLedgerLocked(root, ledger => { ledger.sandboxes[path] = restored; }); return reattached ? 'registration restored' : 'left resumable'; }
|
|
440
456
|
catch (error) { return `REGISTRATION NOT RESTORED (fix ${path} in house.json by hand): ${error instanceof Error ? error.message : String(error)}`; }
|
|
441
457
|
};
|
|
442
458
|
const fail = (message: string): never => { throw new Error(`${message}; nothing removed: ${path} · ${restore()}`); };
|
|
443
|
-
if (!resuming) git(path, 'checkout', '--quiet', '--detach');
|
|
459
|
+
if (!resuming && !detached) git(path, 'checkout', '--quiet', '--detach');
|
|
444
460
|
for (const name of ['HEAD.lock', 'index.lock']) {
|
|
445
461
|
const lock = join(admin, name);
|
|
446
462
|
try { takeLock(lock); held.push(lock); }
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IMAGE-LEDGER-TAGS (RFC-adaptive-graph-ops §9.11) — first slice: the image ledger.
|
|
3
|
+
*
|
|
4
|
+
* The registry tag itself carries only the 12-char commit (docker/harness/build.sh:276), so the
|
|
5
|
+
* identity of an image (why it exists, how long to keep it) lives in a small append-only ledger
|
|
6
|
+
* next to the config that pins it (`<config dir>/image-ledger.jsonl`). Writers append events;
|
|
7
|
+
* readers fold them per tag. A registry tag with no ledger row is `transient` (RFC §9.11).
|
|
8
|
+
*
|
|
9
|
+
* Writers in this slice: the baseline pin (`config set harness.imageCommit <commit>` — the rail
|
|
10
|
+
* pins through that command). Launch · gate · release writers and `image tag` are later slices.
|
|
11
|
+
*
|
|
12
|
+
* Unreadable is never "none": a ledger line that fails to parse is counted, and a registry that
|
|
13
|
+
* cannot be read is reported as `null` (not as an empty tag list).
|
|
14
|
+
*/
|
|
15
|
+
import { spawnSync } from 'node:child_process';
|
|
16
|
+
import { appendFileSync, existsSync, mkdirSync, readFileSync } from 'node:fs';
|
|
17
|
+
import { hostname } from 'node:os';
|
|
18
|
+
import { dirname, join } from 'node:path';
|
|
19
|
+
import { getElanousConfigDir } from '../elanous-config-dir.js';
|
|
20
|
+
|
|
21
|
+
export const IMAGE_ROLES = ['baseline', 'release', 'gate', 'demo', 'candidate', 'transient'] as const;
|
|
22
|
+
export type ImageRole = typeof IMAGE_ROLES[number];
|
|
23
|
+
export const IMAGE_KEEPS = ['pin', 'long', 'short', 'gc'] as const;
|
|
24
|
+
export type ImageKeep = typeof IMAGE_KEEPS[number];
|
|
25
|
+
export const IMAGE_ORIGIN_REASONS = ['rail', 'launch', 'gate', 'release', 'manual', 'config-set'] as const;
|
|
26
|
+
export type ImageOriginReason = typeof IMAGE_ORIGIN_REASONS[number];
|
|
27
|
+
|
|
28
|
+
export interface ImageLedgerEvent {
|
|
29
|
+
readonly v: 1;
|
|
30
|
+
readonly ts: string;
|
|
31
|
+
/** Registry tag = first 12 hex chars of the commit (build.sh:276). */
|
|
32
|
+
readonly tag: string;
|
|
33
|
+
readonly commit: string;
|
|
34
|
+
readonly role: ImageRole;
|
|
35
|
+
readonly keep: ImageKeep;
|
|
36
|
+
readonly origin: { readonly host: string; readonly reason: ImageOriginReason };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface ImageLedgerRead {
|
|
40
|
+
readonly path: string;
|
|
41
|
+
readonly exists: boolean;
|
|
42
|
+
readonly events: readonly ImageLedgerEvent[];
|
|
43
|
+
/** Lines that were present but did not parse as a ledger event. */
|
|
44
|
+
readonly unreadableLines: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface ImageLedgerRow {
|
|
48
|
+
readonly tag: string;
|
|
49
|
+
readonly commit: string;
|
|
50
|
+
readonly role: ImageRole;
|
|
51
|
+
readonly keep: ImageKeep;
|
|
52
|
+
/** Origin of the first event for this tag · null for a registry-only (unledgered) tag — its origin is unknown. */
|
|
53
|
+
readonly origin: { readonly host: string; readonly reason: ImageOriginReason } | null;
|
|
54
|
+
readonly firstTs: string;
|
|
55
|
+
readonly lastTs: string;
|
|
56
|
+
/** For `baseline`: true only for the most recently pinned baseline. */
|
|
57
|
+
readonly current: boolean;
|
|
58
|
+
/** true when the tag is on the registry · false when not · null when the registry was not read. */
|
|
59
|
+
readonly inRegistry: boolean | null;
|
|
60
|
+
/** true when the row comes only from the registry (no ledger event) — always role `transient`. */
|
|
61
|
+
readonly unledgered: boolean;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const HEX = /^[0-9a-f]{12,40}$/u;
|
|
65
|
+
|
|
66
|
+
export function imageLedgerPath(configDir: string = getElanousConfigDir()): string {
|
|
67
|
+
return join(configDir, 'image-ledger.jsonl');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function imageTag(commit: string): string {
|
|
71
|
+
return commit.trim().toLowerCase().slice(0, 12);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function isEvent(value: unknown): value is ImageLedgerEvent {
|
|
75
|
+
if (!value || typeof value !== 'object') return false;
|
|
76
|
+
const e = value as Record<string, unknown>;
|
|
77
|
+
const origin = e.origin as Record<string, unknown> | undefined;
|
|
78
|
+
return e.v === 1 && typeof e.ts === 'string' && typeof e.tag === 'string' && /^[0-9a-f]{12}$/u.test(e.tag)
|
|
79
|
+
&& typeof e.commit === 'string' && HEX.test(e.commit)
|
|
80
|
+
&& (IMAGE_ROLES as readonly unknown[]).includes(e.role) && (IMAGE_KEEPS as readonly unknown[]).includes(e.keep)
|
|
81
|
+
&& !!origin && typeof origin.host === 'string' && (IMAGE_ORIGIN_REASONS as readonly unknown[]).includes(origin.reason);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function readImageLedger(path: string = imageLedgerPath()): ImageLedgerRead {
|
|
85
|
+
if (!existsSync(path)) return { path, exists: false, events: [], unreadableLines: 0 };
|
|
86
|
+
const events: ImageLedgerEvent[] = [];
|
|
87
|
+
let unreadableLines = 0;
|
|
88
|
+
for (const line of readFileSync(path, 'utf8').split('\n')) {
|
|
89
|
+
if (line.trim() === '') continue;
|
|
90
|
+
try {
|
|
91
|
+
const parsed: unknown = JSON.parse(line);
|
|
92
|
+
if (isEvent(parsed)) events.push(parsed); else unreadableLines += 1;
|
|
93
|
+
} catch { unreadableLines += 1; }
|
|
94
|
+
}
|
|
95
|
+
return { path, exists: true, events, unreadableLines };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function appendImageLedgerEvent(event: ImageLedgerEvent, path: string = imageLedgerPath()): void {
|
|
99
|
+
if (!isEvent(event)) throw new Error(`invalid image ledger event: ${JSON.stringify(event)}`);
|
|
100
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
101
|
+
appendFileSync(path, `${JSON.stringify(event)}\n`, { mode: 0o600 });
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** The baseline pin event — what `config set harness.imageCommit <commit>` records. */
|
|
105
|
+
export function baselinePinEvent(commit: string, deps: { now?: () => Date; host?: string } = {}): ImageLedgerEvent {
|
|
106
|
+
const c = commit.trim().toLowerCase();
|
|
107
|
+
if (!HEX.test(c)) throw new Error(`not a commit (hex 12~40): ${commit}`);
|
|
108
|
+
return {
|
|
109
|
+
v: 1,
|
|
110
|
+
ts: (deps.now?.() ?? new Date()).toISOString(),
|
|
111
|
+
tag: imageTag(c),
|
|
112
|
+
commit: c,
|
|
113
|
+
role: 'baseline',
|
|
114
|
+
keep: 'pin',
|
|
115
|
+
origin: { host: deps.host ?? hostname(), reason: 'config-set' },
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Fold events per tag (the last event decides role and keep), then add registry tags that have no
|
|
121
|
+
* ledger event as `transient`. `registryTags === null` means the registry was not read.
|
|
122
|
+
* Non-commit registry tags (`latest`) are moving aliases and are not rows.
|
|
123
|
+
*/
|
|
124
|
+
export function foldImageLedger(events: readonly ImageLedgerEvent[], registryTags: readonly string[] | null): ImageLedgerRow[] {
|
|
125
|
+
const byTag = new Map<string, { first: ImageLedgerEvent; last: ImageLedgerEvent }>();
|
|
126
|
+
// Events for one tag may carry the 12-char tag or the full 40-char commit; the row keeps the longest one seen.
|
|
127
|
+
const fullCommit = new Map<string, string>();
|
|
128
|
+
for (const e of events) {
|
|
129
|
+
const seen = byTag.get(e.tag);
|
|
130
|
+
if (!seen) byTag.set(e.tag, { first: e, last: e });
|
|
131
|
+
else if (e.ts >= seen.last.ts) seen.last = e;
|
|
132
|
+
if (e.commit.length > (fullCommit.get(e.tag)?.length ?? 0)) fullCommit.set(e.tag, e.commit);
|
|
133
|
+
}
|
|
134
|
+
let currentBaseline: string | null = null;
|
|
135
|
+
let currentTs = '';
|
|
136
|
+
for (const e of events) if (e.role === 'baseline' && e.ts >= currentTs) { currentBaseline = e.tag; currentTs = e.ts; }
|
|
137
|
+
const registry = registryTags === null ? null : new Set(registryTags.filter((t) => /^[0-9a-f]{12}$/u.test(t)));
|
|
138
|
+
const rows: ImageLedgerRow[] = [...byTag.values()].map(({ first, last }) => ({
|
|
139
|
+
tag: last.tag,
|
|
140
|
+
commit: fullCommit.get(last.tag) ?? last.commit,
|
|
141
|
+
role: last.role,
|
|
142
|
+
keep: last.keep,
|
|
143
|
+
origin: first.origin,
|
|
144
|
+
firstTs: first.ts,
|
|
145
|
+
lastTs: last.ts,
|
|
146
|
+
current: last.role === 'baseline' && last.tag === currentBaseline,
|
|
147
|
+
inRegistry: registry === null ? null : registry.has(last.tag),
|
|
148
|
+
unledgered: false,
|
|
149
|
+
}));
|
|
150
|
+
if (registry) for (const tag of [...registry].sort()) {
|
|
151
|
+
if (byTag.has(tag)) continue;
|
|
152
|
+
rows.push({ tag, commit: tag, role: 'transient', keep: 'short', origin: null, firstTs: '', lastTs: '', current: false, inRegistry: true, unledgered: true });
|
|
153
|
+
}
|
|
154
|
+
return rows.sort((a, b) => (b.lastTs || '').localeCompare(a.lastTs || '') || a.tag.localeCompare(b.tag));
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export type RegistryRead = { readonly ok: true; readonly tags: readonly string[] } | { readonly ok: false; readonly reason: string };
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Read `elanous-harness` tags from the registry the way the rail does (`/v2/elanous-harness/tags/list`
|
|
161
|
+
* on the registry node's localhost port). `sshHost` runs it on that node; otherwise on this machine.
|
|
162
|
+
*/
|
|
163
|
+
export function readRegistryTags(opts: { port: string; sshHost?: string; run?: (cmd: string, args: readonly string[]) => { status: number | null; stdout: string; stderr?: string } }): RegistryRead {
|
|
164
|
+
const url = `http://localhost:${opts.port}/v2/elanous-harness/tags/list`;
|
|
165
|
+
const run = opts.run ?? ((cmd, args) => {
|
|
166
|
+
const r = spawnSync(cmd, [...args], { encoding: 'utf8', timeout: 25_000 });
|
|
167
|
+
return { status: r.status, stdout: r.stdout ?? '', stderr: r.stderr ?? (r.error ? String(r.error) : '') };
|
|
168
|
+
});
|
|
169
|
+
const r = opts.sshHost
|
|
170
|
+
? run('ssh', ['-o', 'BatchMode=yes', '-o', 'ConnectTimeout=10', opts.sshHost, `curl -fsS --max-time 15 ${url}`])
|
|
171
|
+
: run('curl', ['-fsS', '--max-time', '15', url]);
|
|
172
|
+
if (r.status !== 0) return { ok: false, reason: `registry read failed (exit ${r.status ?? 'timeout'})${r.stderr ? `: ${r.stderr.trim().slice(0, 200)}` : ''}` };
|
|
173
|
+
try {
|
|
174
|
+
const tags = (JSON.parse(r.stdout) as { tags?: unknown }).tags;
|
|
175
|
+
if (tags === null) return { ok: true, tags: [] };
|
|
176
|
+
if (!Array.isArray(tags) || !tags.every((t) => typeof t === 'string')) return { ok: false, reason: 'registry answered without a tags list' };
|
|
177
|
+
return { ok: true, tags };
|
|
178
|
+
} catch { return { ok: false, reason: 'registry answered non-JSON' }; }
|
|
179
|
+
}
|