elanous 0.2.34 → 0.2.35
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/{L04y73dlwKeTW-GetaTF3 → T1mBQqmkwKDLGtyisDCOd}/_buildManifest.js +1 -1
- package/apps/pwa/out/_next/static/chunks/{5014f970-22bef0b5210f7d60.js → 0368f25d-ad84af92fefee68f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1165.bad13fc9151a7288.js +1 -0
- package/apps/pwa/out/_next/static/chunks/13.02a07436dec15f89.js +32 -0
- package/apps/pwa/out/_next/static/chunks/{2124.ed5bf480cea7f85b.js → 1530.0e612369b6fd7ea0.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1540.af38731fa7c4a3e0.js +41 -0
- package/apps/pwa/out/_next/static/chunks/1674.2a65fd5668e1cc5f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1850.f2b470ba5a471da0.js +24 -0
- package/apps/pwa/out/_next/static/chunks/193.62da94cd0b88323b.js +74 -0
- package/apps/pwa/out/_next/static/chunks/1975.260f6d249fac5f7c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1982-67cd8dfe8682325f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1993-c0007b7ad0c0ef41.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1999-bb415351c8a2846c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1c735de4.f993a30bbd3c57c4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1dc5a567.9b092bfae3d7099b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8368.014caa8de2f5af34.js → 2018.a741297bce759490.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2085.8f950b4bda1cbbc7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2159-6179a961376956b9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/216.b5c2a421aa1d28a5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2251.49fbe369f74c372c.js +148 -0
- package/apps/pwa/out/_next/static/chunks/2286-2939577360d3dcc0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2625.5d870aa999f33db1.js +34 -0
- package/apps/pwa/out/_next/static/chunks/2668.34d21539e62bf111.js +77 -0
- package/apps/pwa/out/_next/static/chunks/2833-0fe47af99ed13c29.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2930.d0edb4f108787ef7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2973.77318e7d85f59477.js +62 -0
- package/apps/pwa/out/_next/static/chunks/3103.0df8da390948bb84.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3123.2bee9ba15d320bd2.js +90 -0
- package/apps/pwa/out/_next/static/chunks/3162-6cf652bf7124d8cf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1644.35dd25240ae24e4b.js → 3408.21d1570602f4b6c0.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/3577-7340009f7007a795.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3121.779fafe3e44612c5.js → 3589.5444a06bcf6e775c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/359-2250789a2d3499e7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3618.f514792d6c59d7fd.js +36 -0
- package/apps/pwa/out/_next/static/chunks/3669.343f9119aa276001.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9131-fce7256c4180c4a5.js → 3677-4215f826f6aa68e5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3736.c2efad489604cc3b.js +79 -0
- package/apps/pwa/out/_next/static/chunks/3817.f9aeab2bb6675d73.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3f7c4ee5.3dde54f01a816613.js +122 -0
- package/apps/pwa/out/_next/static/chunks/4182-0b9b1251c29cc0cf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4346.8601952e73bf5803.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6053-d893974de92e6198.js → 4441-875b97f5821ed58a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4582.bd68683c076649e3.js +286 -0
- package/apps/pwa/out/_next/static/chunks/4605-6e623f55aec77422.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4607.5cc27468628eb4bf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4627.f07508b3936fd6c0.js +24 -0
- package/apps/pwa/out/_next/static/chunks/{1895-60ed0e341c658bd4.js → 4705-194e64e47ca0fbdb.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{5576-82d3703636d5c4b9.js → 4718-17d2af636796a330.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/4747-c22262c093e46807.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4775-178215aed0daba61.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4815-6ffb8c3c991cc45d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4834-beca8b47ad29a898.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5023-fb4802b6d2492b02.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7431.3051e9b8a4372d2d.js → 5101.e32282718f99ed6f.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/5154.a118f3957ed14e36.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5210-a2e0015e3d4d5d9c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5356.84ac712cc15928bf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5401.22352141b6fd336e.js +23 -0
- package/apps/pwa/out/_next/static/chunks/{2987-30cf9c2eb7b8bdd5.js → 5483-dd5d7697f6bdb106.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5637.281e5dec4ea6ecf1.js +94 -0
- package/apps/pwa/out/_next/static/chunks/5662.02eaa029af05ad46.js +1 -0
- package/apps/pwa/out/_next/static/chunks/56d372f2.681ab4ec4c998fae.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5760.22456f433c6f42c0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7386.5d8e7e42e818dfb6.js → 5852.db1f7449f4f74612.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/5891-0f11a7d7362a9072.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5943.264d14a44ca32513.js +1 -0
- package/apps/pwa/out/_next/static/chunks/604-1b3c39df083c02aa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6121-5f97072330738caf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6193.07d8ca5af4b09086.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6203-b3fea3a60d69ff14.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3110.0a7065f25c841818.js → 6236.e18cced2d5ba6afb.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/6286.d638fb5953e2e757.js +8 -0
- package/apps/pwa/out/_next/static/chunks/630.fe5a46428d63b013.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6301.ae0245f507e4395e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6673.fbbe5699f6ea5991.js +27 -0
- package/apps/pwa/out/_next/static/chunks/6777-f6d301efb5de828d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6786.423f24c88de034c7.js +93 -0
- package/apps/pwa/out/_next/static/chunks/6854-cef64a948c5e122d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6866-79aa5b4e47cac6aa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6b89c8bd.24df687a820500b3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7037-fe27f001db30d323.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7080.2d826860534fd88e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7118.8731be28c3ebb14b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7196.44241bc1df3e26fe.js +149 -0
- package/apps/pwa/out/_next/static/chunks/7235.c7c4e6cb5c693c2d.js +104 -0
- package/apps/pwa/out/_next/static/chunks/{7005.1623d2aea78eb85f.js → 7385.50a308a477bdfc21.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{6086.913a69e6fa3e4a8b.js → 7644.17e37ba6f1d4226e.js} +4 -4
- package/apps/pwa/out/_next/static/chunks/7706-58bfb1546637f217.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7839.5b91455338c9fd3f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7857-31f13ee96f4011aa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/81.f74cb60312fe4001.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8219.baa715d1deee80e2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8256.e3ac8421a71f82f4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8310-444a6a895c402f12.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8423.f7dda8aca4fcaae5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7193-cf355cca5ba6c5d5.js → 8437-8d5492094b166cfa.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{2554.3b5e200f5f5cfcd6.js → 8444.4c26b379ca2b9596.js} +4 -4
- package/apps/pwa/out/_next/static/chunks/8496.75649af8652c74f4.js +226 -0
- package/apps/pwa/out/_next/static/chunks/8557-e7898769499cd56f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8576.65bb654b524f3614.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8609.558ce2f8f9aea2be.js +64 -0
- package/apps/pwa/out/_next/static/chunks/8614.caec96cfb91f4ae7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2547.dd27303624cb4da8.js → 8637.47f5b35afcae5e6b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8714.ff0b7ce3ec3e3ae2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8732.367f44a916202353.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9141-7fd1f73a5400bdc2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/921.c4267d0d69a65404.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9550.d5f76828f972058c.js → 9214.63a510d39178e997.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/9261.3962c3321b047691.js +115 -0
- package/apps/pwa/out/_next/static/chunks/936-26efd1a0efdd44d3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8548.036c344973968847.js → 938.66ad1ef0e5abae1c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{3414.172ad4ca2d9b08e8.js → 9512.5a76207a730e0491.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/9627-4b9efe616b27e2e2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/967-2aa39b2d04b4c690.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9734.40eb1e558c29ccea.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9908.3aa54f7a586a73dc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6740.43de73fc427c9fe2.js → 9952.5daf5f94063a2b3e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9954-ca8d29752bdbb45a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9974-507fa67cb94066dd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-ce9e00f7fb45506a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-fa59d6d17daa3389.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-53f68bbe7bd4b774.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-1c500fbb9f4a7a04.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-99d72673aa5c307c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-d2d5adc1e9484c88.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-c607c5aae637f1cc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-fa8c23596d555c0f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-bdab8c5e855c8416.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-406b47745b53f269.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-a4276386b7051f50.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-8a26bab9ad375f25.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-775f48aa1dc7a2d5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-1c4dd5f959d0c558.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-c48938a8377c9f70.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-ce9e00f7fb45506a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/page-c6e7905859425aa9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-c280f4252452e830.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-cf8c57498f20970a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-ca3a17caf409fc83.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live-run/{page-d7bff0f0905a662c.js → page-26e6c18a1a750cf6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-3abc872cc6aeee39.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-f7481b7b2174fe3d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-1ae7ef8077680337.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/page-47c5d4db587266bd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-415f1e243c7ccb13.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-73f380f17b76f95c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-2991073ad66ee2d9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-1f5a60f70cb4dd0c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fbad60d1e358fb2c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-bac7dee94e5eae4a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-398047d6949e8ca5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-2d33a2231540ca89.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-5bf1e6bf70bcb96c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-ba59c19376a0917d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-111f187139add30f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-acaae197d5be5779.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-0df20b0f546a0d7d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-ce9e00f7fb45506a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-27e1202d24b8fe35.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-bda78c50a78363e5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-da592691293d9c9f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-f832994317a2e9f0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/{page-df3445b207ceceab.js → page-dc0a532f5b9196ec.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-cfccbbeae0473404.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-527e2073af84c81b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-bac86e9112671357.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-6d59dff410e9d76a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-53a61f8be09f02d2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-db8f559e763c17f9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-c02aa4cc66455093.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9a8cb466.cc9fc9e6e46038aa.js → b2f6ba4d.fb56fdbbf798df33.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/b6705a4f-b9a9047de9eecfd0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4fa33ebd.21c1bd5b294496e6.js → cbe38a15.5a032598e39970c6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{abb4b98b.163e0f56e84ae35d.js → d06f9160.a3d9062f95de8ad7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{9b48c871.204d5d13e7809bd0.js → da5176c6.6773b6c5994e1d99.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/framework-50e6acebc630849b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-7a1ae342d5ed75e6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-7ad7d505ba1f92b4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-02edba75232cb235.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-97d6ebae3e8eee4a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/webpack-e986adaa429163e8.js +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +22 -22
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +22 -22
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +22 -22
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +21 -21
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +21 -21
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +22 -22
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +21 -21
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +22 -22
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +22 -22
- package/apps/pwa/out/decisions/index.html +1 -1
- package/apps/pwa/out/decisions/index.txt +21 -21
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +22 -22
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +22 -22
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +22 -22
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +22 -22
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +21 -21
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +21 -21
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +22 -22
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +22 -22
- package/apps/pwa/out/live-run/index.html +1 -1
- package/apps/pwa/out/live-run/index.txt +22 -22
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +21 -21
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +21 -21
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +21 -21
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +22 -22
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +22 -22
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +21 -21
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +21 -21
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +21 -21
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +21 -21
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +22 -22
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +22 -22
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +21 -21
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +22 -22
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +22 -22
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +22 -22
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +22 -22
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +22 -22
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +21 -21
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +22 -22
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +22 -22
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +22 -22
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +22 -22
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +22 -22
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +22 -22
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +22 -22
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +22 -22
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +22 -22
- package/bin/elanous.mjs +3 -2
- package/catalog/providers/opengateway.yaml +25 -0
- package/docs/goal-context/01-goal-authoring-checks.md +8 -0
- package/docs/goal-context/03-verification-menu.md +24 -0
- package/docs/goal-context/04-mock-scenarios.md +100 -0
- package/docs/goal-context/README.md +4 -0
- package/graphs/catalog/catalog.yaml +1 -1
- package/graphs/default-loop.yaml +57 -3
- package/graphs/document-loop.yaml +51 -3
- package/graphs/implement-loop.yaml +53 -4
- package/graphs/journey/ta-journey.yaml +11 -1
- package/graphs/journey/units/author-dialogue/author-dialogue.yaml +42 -0
- package/graphs/journey/units/author-dialogue/observe.ts +96 -0
- package/graphs/journey/units/author-dialogue/recipes.yaml +10 -0
- package/graphs/operate-loop.yaml +51 -3
- package/graphs/research-loop.yaml +137 -3
- package/graphs/test-diet/test-diet.yaml +3 -0
- package/package.json +1 -1
- package/scripts/coord-role-pack.ts +19 -6
- package/scripts/hq/standby-snapshot.ts +17 -12
- package/scripts/release-loop/docs-land-node.ts +21 -5
- package/scripts/release-loop/gate-baseline-receipt.ts +155 -0
- package/scripts/release-loop/gate-node.ts +181 -22
- package/scripts/release-loop/gate-partial.ts +65 -11
- package/scripts/release-loop/gate-radius-live.ts +177 -0
- package/scripts/release-loop/gate-radius-shadow.ts +1 -1
- package/scripts/release-loop/node-verdict.ts +12 -1
- package/scripts/release-loop/npm-publish-node.ts +66 -12
- package/scripts/release-loop/probe-pod-host-dirs-live.ts +5 -0
- package/scripts/release-loop/pwa-remote.ts +166 -0
- package/scripts/release-loop/pwa-scenarios-node.ts +19 -3
- package/scripts/release-loop/resume-release.ts +41 -4
- package/scripts/release-loop/unattended-release.ts +2 -0
- package/scripts/test-diet/lib.ts +6 -1
- package/scripts/test-diet/node.ts +194 -13
- package/scripts/ux-sim.ts +39 -0
- package/src/agent/self-ambient-cache.ts +8 -0
- package/src/agent-substrate/acp-reviewer.ts +39 -4
- package/src/agent-substrate/pr-reviewer.ts +17 -0
- package/src/agent-substrate/self-review-cli.ts +62 -13
- package/src/autopilot/pr-manager.ts +215 -36
- package/src/budget/run-usage-rollup.ts +1 -0
- package/src/chat/index.ts +0 -2
- package/src/chat/pickers/modal-runtime.ts +10 -27
- package/src/cli/agent-cli.ts +13 -2
- package/src/cli/doctor-cli.ts +4 -1
- package/src/cli/light-entry-route.ts +46 -6
- package/src/cli/pod-cli.ts +77 -3
- package/src/cli/pr-cli.ts +5 -2
- package/src/cli/pr-land-pod-shadow-job.ts +3 -0
- package/src/cli/release-checklist-read-cli.ts +79 -0
- package/src/cli/release-cli.ts +72 -102
- package/src/cli/remote-resolve.ts +1 -1
- package/src/cli/root-tui-entry.ts +48 -0
- package/src/cli/sim-cli.ts +22 -0
- package/src/cli/tasks-cli.ts +540 -31
- package/src/cli/usage-cli.ts +13 -11
- package/src/clipboard/history.ts +179 -13
- package/src/config.ts +8 -1
- package/src/dashboard/chat/acp-chat.ts +8 -0
- package/src/dashboard/index.ts +78 -10
- package/src/dashboard/render/theme-resolver.ts +33 -4
- package/src/domains/schedule-registry.ts +16 -2
- package/src/git-fs/idle-refresh.ts +76 -0
- package/src/git-fs/index.ts +18 -7
- package/src/git-fs/worktree.ts +42 -4
- package/src/graph-kinds/registry.ts +2 -2
- package/src/graph-runner/runner.ts +153 -10
- package/src/grok/credential.ts +10 -3
- package/src/harness/harness-cli-command.ts +112 -28
- package/src/harness/harness-incidents.ts +33 -0
- package/src/harness/launch-utterance-ledger.ts +208 -0
- package/src/impl-discipline/detector.ts +16 -4
- package/src/impl-discipline/system-prompt.ts +11 -0
- package/src/impl-discipline/types.ts +2 -2
- package/src/index.ts +496 -143
- package/src/intake-plane/already-built-hint.ts +4 -2
- package/src/intake-plane/requirement-funnel.ts +29 -1
- package/src/light-cli.ts +27 -11
- package/src/llm/opengateway-models.ts +11 -0
- package/src/llm/provider-credentials.ts +14 -0
- package/src/llm/provider-detect.ts +8 -1
- package/src/llm/runtime-model-family.ts +2 -1
- package/src/llm.ts +213 -56
- package/src/model-tier/llm-tier-map.ts +15 -1
- package/src/native-tool-catalog.ts +15 -1
- package/src/nexus/api/loop-resources.ts +13 -1
- package/src/oauth/codex.ts +7 -0
- package/src/ops/overview-collect.ts +18 -3
- package/src/ops/overview.ts +20 -7
- package/src/pr-tool/census.ts +261 -27
- package/src/pty-shell/pty-manifest.ts +7 -0
- package/src/release-loop/checklist.ts +24 -0
- package/src/release-loop/landed-but-yellow.ts +129 -5
- package/src/release-loop/light-rc.ts +1 -0
- package/src/release-loop/rehearsal.ts +2 -2
- package/src/repl/index.ts +174 -17
- package/src/repl/ops-msg-bridge.ts +32 -0
- package/src/self-dev/ask-launch-flow.ts +44 -5
- package/src/self-dev/cli-help-text.ts +17 -0
- package/src/self-dev/dev-cli.ts +11 -9
- package/src/self-dev/draft-residue.ts +37 -1
- package/src/self-dev/draft-sweep.ts +137 -11
- package/src/self-dev/draft-triage-rules.ts +107 -0
- package/src/self-dev/fleet-reduce.ts +3 -2
- package/src/self-dev/graph-front-nodes.ts +112 -0
- package/src/self-dev/graph-journey-nodes.ts +1 -0
- package/src/self-dev/harness-run-cli.ts +3 -15
- package/src/self-dev/orchestrate-cli-text.ts +10 -0
- package/src/self-dev/review-provider-fallback.ts +35 -1
- package/src/self-dev/self-orchestrate-runtime.ts +1 -11
- package/src/self-dev/shard-aggregate.ts +42 -5
- package/src/self-dev/shard-run.ts +8 -1
- package/src/self-implement/blocked-draft-policy.ts +20 -0
- package/src/self-implement/classify-shadow.ts +3 -7
- package/src/self-implement/doctor-visit.ts +18 -1
- package/src/self-implement/gate-baseline.ts +56 -17
- package/src/self-implement/gate-cli.ts +1 -0
- package/src/self-implement/gate-remote.ts +31 -20
- package/src/self-implement/goal-author-decompose.ts +275 -47
- package/src/self-implement/goal-author.ts +250 -0
- package/src/self-implement/goal-elaborate.ts +295 -0
- package/src/self-implement/goal-refine.ts +270 -0
- package/src/self-implement/graph-overlay-yaml.ts +14 -3
- package/src/self-implement/orchestrator.ts +152 -45
- package/src/self-implement/rework-budget-judgment.ts +5 -8
- package/src/self-implement/run-stop.ts +10 -1
- package/src/self-implement/running-runs.ts +29 -1
- package/src/self-implement/scope-adjacency.ts +113 -0
- package/src/self-implement/seams.ts +127 -24
- package/src/self-implement/stop-autoheal.ts +87 -1
- package/src/session-runtime/index.ts +1 -1
- package/src/setup/llm-env-detect.ts +5 -0
- package/src/skills/tools/agent.ts +140 -14
- package/src/skills/tools/msg-ask.ts +100 -0
- package/src/skills/tools/ops-ledger.ts +2 -1
- package/src/steward/hold-hygiene.ts +24 -6
- package/src/steward/triage-budget.ts +194 -0
- package/src/steward/triage.ts +20 -3
- package/src/task-agent/actions.ts +69 -1
- package/src/task-agent/green-sweep.ts +4 -0
- package/src/task-agent/live-moves.ts +160 -4
- package/src/task-agent/parent-host.ts +123 -3
- package/src/task-agent/shadow.ts +7 -1
- package/src/task-agent/task-hand.ts +54 -41
- package/src/task-agent/tier-split.ts +127 -0
- package/src/task-agent/work-order-build.ts +2 -0
- package/src/task-agent/work-order.ts +3 -2
- package/src/task-orchestrator/generator-prompt.ts +60 -11
- package/src/task-orchestrator/surfaces/pod-account-broker.ts +33 -0
- package/src/task-orchestrator/surfaces/pod-child-providers.ts +1 -1
- package/src/task-orchestrator/surfaces/pod-command-job.ts +45 -7
- package/src/task-orchestrator/surfaces/pod-image-source.ts +89 -1
- package/src/task-orchestrator/surfaces/pod-lease.ts +17 -8
- package/src/task-orchestrator/surfaces/pod-manifest-test-normalize.ts +8 -2
- package/src/task-orchestrator/surfaces/pod-provenance.ts +76 -0
- package/src/task-orchestrator/surfaces/pod-source-receive.ts +18 -0
- package/src/task-orchestrator/surfaces/pod-warm-provenance.ts +81 -0
- package/src/task-orchestrator/surfaces/pod-warm-worker-pod.ts +6 -2
- package/src/task-orchestrator/surfaces/pod-warm-worker-protocol.ts +3 -0
- package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +37 -5
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +368 -36
- package/src/task-orchestrator/surfaces/self-implement.ts +23 -0
- package/src/tool-runtime/ops-ledger-runtimes.ts +10 -2
- package/src/tui/README.md +2 -0
- package/src/tui/chat/background-job-status.ts +54 -0
- package/src/tui/chat/log-view.ts +80 -0
- package/src/tui/entry.ts +406 -0
- package/src/tui/input/composer.ts +144 -0
- package/src/tui/seat-mail-boot.ts +59 -0
- package/src/tui/slash/compact.ts +100 -0
- package/src/tui/slash/new-session.ts +35 -0
- package/src/tui-client/local-chat-turn.ts +239 -0
- package/src/tui-client/local-harness-command.ts +63 -0
- package/src/tui-client/local-provider-command.ts +184 -0
- package/src/user-config.ts +4 -2
- package/src/ux-sim/event-monkey.ts +378 -0
- package/src/ux-sim/fake-bin.ts +42 -7
- package/src/ux-sim/fault-nextmove-dispatch.ts +266 -0
- package/src/ux-sim/fault-pod-codex-503.ts +211 -0
- package/src/ux-sim/fault-scope-escape-lift.ts +189 -0
- package/src/version/packed-revision.json +1 -1
- package/apps/pwa/out/_next/static/chunks/1162.c69abda138c7ce43.js +0 -79
- package/apps/pwa/out/_next/static/chunks/1449-1fede62af84c3e43.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1598-13fc845935e816df.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1639.62c5e16fa20d242f.js +0 -94
- package/apps/pwa/out/_next/static/chunks/1661-adb8b2babc9aaf8c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2147.107c0fc6146fb15a.js +0 -23
- package/apps/pwa/out/_next/static/chunks/2202-ef1c9b000c3074fc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2247.1e326638ee43e672.js +0 -115
- package/apps/pwa/out/_next/static/chunks/2268.56e1e3d58180abae.js +0 -8
- package/apps/pwa/out/_next/static/chunks/2469-606819c812414021.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2593.e53c1620c0bb3599.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2690.e96671114222676a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2898-79ee1a5cc0cd9d13.js +0 -1
- package/apps/pwa/out/_next/static/chunks/298.26099a9f65e5c8ff.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2992.51fc2a7295bd8139.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3001-5a52788c813b6113.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3014-291245fba9df7b5b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3068.ee78201f0251a18d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3083.cd399a17e9365f78.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3103.84470c8445962f04.js +0 -34
- package/apps/pwa/out/_next/static/chunks/3117-e1c474683fd1f824.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3251-2c17bd5a91f8de2e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/326.3fb32322bb2ddf6d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3411-f87314c6c66ebc73.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3421-7ff7fbaa156985a7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3498.cf2a15a8a9e5b926.js +0 -226
- package/apps/pwa/out/_next/static/chunks/381.f55dc63ea87735e6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3819.163d4777f0a597f6.js +0 -32
- package/apps/pwa/out/_next/static/chunks/3837.722e053fb9f321ad.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4129-ab0a6189f1778633.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4361.93d14ba594b3a035.js +0 -104
- package/apps/pwa/out/_next/static/chunks/4364.7a7eb19cceb67d2f.js +0 -286
- package/apps/pwa/out/_next/static/chunks/4406.d71f506f7c3ee9d3.js +0 -149
- package/apps/pwa/out/_next/static/chunks/4461.2ad1514180ae7e29.js +0 -148
- package/apps/pwa/out/_next/static/chunks/4479.bd309cbe0e3842ee.js +0 -64
- package/apps/pwa/out/_next/static/chunks/4522-05966f777d66d13b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4543-9a3e9812e845de65.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4557.f8f1d84a0d90d590.js +0 -74
- package/apps/pwa/out/_next/static/chunks/4589-79b11542179ef58c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4633-9f8f9f88ffbb8f5c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4831.23af4d10c0697b5e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4983.5951b85724f61f2a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5028-f0722e6114c313e7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5249.96a848656b582a93.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5311.16e0ffbfa5468b80.js +0 -62
- package/apps/pwa/out/_next/static/chunks/5446.edf141cff9ef647d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5489.ec2dd1b5272a19dc.js +0 -24
- package/apps/pwa/out/_next/static/chunks/559.df87695df637920c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5618-5c88f2f70178c4ad.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5764.05487bf0b73ddb6b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5930.e678826c3f831aab.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5dfb57f7.f2e3305326202474.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6200-cefd6f33f59d765e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6314-edeba650ae686ac9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6316-bc9a9b74d7842d79.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6476-13b9779b73528008.js +0 -1
- package/apps/pwa/out/_next/static/chunks/666-04db3ac4c8e3c66f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/670.682e55106ef915a6.js +0 -77
- package/apps/pwa/out/_next/static/chunks/6816.b7ba9d0a1a4a46a7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/690-fbbcc777f189af37.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6989.0d3d4020e5cb3df6.js +0 -90
- package/apps/pwa/out/_next/static/chunks/7217-d17393fe7af78fa6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7232.24edbd938a69c8d0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7295cd1e.b4b557908888eeee.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7323.adb02de21991883c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7448.478958ffbbba0ac0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7537.ba2208c5c656b8a3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7562.948255cbc3e79d33.js +0 -41
- package/apps/pwa/out/_next/static/chunks/7596-962ae6db6cfc9c83.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7671.cff51beac212a2ba.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7883.3916734ee16d3e58.js +0 -27
- package/apps/pwa/out/_next/static/chunks/7952.b192c31b6a0e00cc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/801.5b24e81eded22d1b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8176.8c9a8613f5a01ac1.js +0 -36
- package/apps/pwa/out/_next/static/chunks/819e78a6.fa3dd7f7da19bf61.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8220-78978450c3183d8d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8221-eb60b5bc692a3361.js +0 -1
- package/apps/pwa/out/_next/static/chunks/826.e9a26064f474949e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8459-a351487a4480f584.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8482-75823d513a4ce151.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8488.8da515248cda3f3a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8584.388a1779d9b203af.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8637.a55e098e61ffefb8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8817.61bb7ffbba502ecb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9207-8e6a12db30373f32.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9263-00ae932c62696a86.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9318.caafc3a130e8769c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/932.da3fb55951882562.js +0 -93
- package/apps/pwa/out/_next/static/chunks/9433.002f8426ac75b6b7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9464.dd631a5a14b1d660.js +0 -24
- package/apps/pwa/out/_next/static/chunks/9739.8d5f36ac67a3b236.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9743.beeabb9d5f86abe4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9843-a23d3f32dc43f715.js +0 -1
- package/apps/pwa/out/_next/static/chunks/994-28211077c7c587d5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-3b4c947f174e33d8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-369d5ce99483f859.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-63c0ed0eb26e5fce.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-62802c6003bc85cc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-ecb41560d4e77be9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-7b65ad8130c93e95.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-8755477c15a9060c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-5725447dd972c471.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-b979de0591a69375.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-6d0796bf4633751a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-59c31dc0394e67b4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-12dfc95f6cc5c718.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-25788244e66aa4dd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-a2555dc705466322.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-4fb31eb476dcbad3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-3b4c947f174e33d8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-578729adbeaa40cb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-a1d20fffbec35205.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-4a558f94516b8004.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-9e3834705b86145e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-cb1c5fe746ea4014.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-9ee320507356582a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-66fd183b74835886.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-3272a064a01a9927.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-f281b7a7f6ef2529.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-d45a65f8b7124fcd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-e9c1fe684dd19a66.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-b8322932fc6e34de.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-82c66d9af2290575.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-63c6752009c97b05.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-bb33d42ebc358aec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-7e4c9379197d7214.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-38bf1e9a7001732b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-2e5b752012389d56.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-398c0faa173203f7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-002f53a965b6445c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-7f75997f61a26269.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-3b4c947f174e33d8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-05631753294f23fb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-edd700fd2cc3d7f8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-105f83ecb4594365.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-5823e87d2017f19c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-b51ca09566c03de7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-8aacd5ded5dbac60.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-bae3365a0b3c6a8e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-9ed52b48ff1aa7fd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-b6f8cad35fe00946.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-6d25ceda500d2457.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-b6401a5f2c962fe8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/ebf6cf5e.0d2f887b040e80fb.js +0 -122
- package/apps/pwa/out/_next/static/chunks/f09ec7d7-d29abe23edcbb193.js +0 -1
- package/apps/pwa/out/_next/static/chunks/ffdc3a04.b9c23c1b28dd4a96.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-9322cb8a56ac081c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-2fbab294e49a1561.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-f36729f56621a8f3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-2a62e450b47c6114.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-5e17f7b445f34d43.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-b4263e2190ea7adf.js +0 -1
- /package/apps/pwa/out/_next/static/{L04y73dlwKeTW-GetaTF3 → T1mBQqmkwKDLGtyisDCOd}/_ssgManifest.js +0 -0
|
@@ -17,9 +17,10 @@ import { getDefaultLogStore, type LogStore } from '../../mss/logging/log-store.j
|
|
|
17
17
|
import { podBunCacheVolume } from './pod-bun-cache.js';
|
|
18
18
|
import { POD_JOB_TTL_AFTER_FINISHED_SECONDS } from './pod-lease.js';
|
|
19
19
|
import { podHostDirsCheckScript, podInstallSlotsVolume, prepareHostDirsOnce, type HostDirsPrepResult, podHostDirsScript } from './pod-install-slots.js';
|
|
20
|
-
import { type PodPoolMember, PodPoolScheduler, resolvePodPoolSpec, parsePodPool, checkPodPool, syncPoolImages, type PoolKubectl } from './pod-pool.js';
|
|
20
|
+
import { type PodPoolMember, PodPoolScheduler, resolvePodPoolSpec, parsePodPool, checkPodPool, syncPoolImages, registryImageRef, type PoolKubectl } from './pod-pool.js';
|
|
21
21
|
import { podSkillsDigest, readSkillEnvFiles, resolvePodSkills } from './pod-skills.js';
|
|
22
22
|
import { podSourceScript, type PodSource } from './pod-source-receive.js';
|
|
23
|
+
import { podProvenanceMetadata, type PodProvenance } from './pod-provenance.js';
|
|
23
24
|
import { seedMirrorOnce, type MirrorSeedResult } from './pod-mirror-seed.js';
|
|
24
25
|
import {
|
|
25
26
|
POD_CHILD_REQUESTS,
|
|
@@ -201,6 +202,8 @@ if [ -d "$HOME/outbox" ]; then
|
|
|
201
202
|
fi`;
|
|
202
203
|
|
|
203
204
|
export interface PodCommandJobInput {
|
|
205
|
+
provenance?: Partial<PodProvenance>;
|
|
206
|
+
provenanceGate?: boolean;
|
|
204
207
|
/** POD1 — this launch's own label; cleanup selects by it, never by name alone. */
|
|
205
208
|
launch?: string;
|
|
206
209
|
name: string;
|
|
@@ -236,8 +239,21 @@ function bashSingleQuote(value: string): string {
|
|
|
236
239
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
237
240
|
}
|
|
238
241
|
|
|
242
|
+
function assertGateProvenanceException(gate: boolean | undefined, provenance: Partial<PodProvenance> | undefined,
|
|
243
|
+
clone: boolean | undefined, source: PodSource | undefined): void {
|
|
244
|
+
if (!gate) return;
|
|
245
|
+
const shardEntrance = process.env.ELANOUS_RELEASE_ENTRANCE?.trim() || 'release-run';
|
|
246
|
+
const shard = provenance?.kind === 'gate-shard' && provenance.entrance === shardEntrance;
|
|
247
|
+
const recheck = provenance?.kind === 'gate-recheck' && provenance.entrance === 'release-recheck';
|
|
248
|
+
if (!(shard || recheck) || clone !== true || source?.kind !== 'commit') {
|
|
249
|
+
throw new Error('pod provenance gate exception requires a release shard/recheck commit Job');
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
239
253
|
/** 명령 Job 매니페스트. `skills`·`llm` 을 안 주면 Secret 키를 하나도 참조하지 않는다. */
|
|
240
254
|
export function podCommandJobManifest(o: PodCommandJobInput): Record<string, unknown> {
|
|
255
|
+
assertGateProvenanceException(o.provenanceGate, o.provenance, o.clone, o.source);
|
|
256
|
+
const provenance = podProvenanceMetadata(o.provenance, o.provenanceGate);
|
|
241
257
|
const skillNames = [...new Set(o.skills.map((s) => s.trim()).filter(Boolean))];
|
|
242
258
|
for (const name of skillNames) {
|
|
243
259
|
if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/u.test(name)) throw new Error(`pod command skill name rejected: ${name}`);
|
|
@@ -284,13 +300,13 @@ export function podCommandJobManifest(o: PodCommandJobInput): Record<string, unk
|
|
|
284
300
|
return {
|
|
285
301
|
apiVersion: 'batch/v1',
|
|
286
302
|
kind: 'Job',
|
|
287
|
-
metadata: { name: o.name, namespace: o.namespace, labels: { 'elanous.substrate': 'pod', 'elanous.job': o.name, 'elanous.kind': 'command', ...(o.launch ? { 'elanous.launch': o.launch } : {}) } },
|
|
303
|
+
metadata: { name: o.name, namespace: o.namespace, labels: { 'elanous.substrate': 'pod', 'elanous.job': o.name, 'elanous.kind': 'command', ...(o.launch ? { 'elanous.launch': o.launch } : {}), ...provenance?.labels }, ...(provenance ? { annotations: provenance.annotations } : {}) },
|
|
288
304
|
spec: {
|
|
289
305
|
backoffLimit: 0,
|
|
290
306
|
ttlSecondsAfterFinished: POD_JOB_TTL_AFTER_FINISHED_SECONDS,
|
|
291
307
|
activeDeadlineSeconds: o.deadlineSeconds,
|
|
292
308
|
template: {
|
|
293
|
-
metadata: { labels: { 'elanous.job': o.name, 'elanous.kind': 'command', ...(o.launch ? { 'elanous.launch': o.launch } : {}) } },
|
|
309
|
+
metadata: { labels: { 'elanous.job': o.name, 'elanous.kind': 'command', ...(o.launch ? { 'elanous.launch': o.launch } : {}), ...provenance?.labels }, ...(provenance ? { annotations: provenance.annotations } : {}) },
|
|
294
310
|
spec: {
|
|
295
311
|
restartPolicy: 'Never',
|
|
296
312
|
securityContext: { runAsUser: 1000, fsGroup: 1000 },
|
|
@@ -306,6 +322,7 @@ export function podCommandJobManifest(o: PodCommandJobInput): Record<string, unk
|
|
|
306
322
|
...(o.runId ? [{ name: 'ELANOUS_RUN_ID', value: o.runId }] : []),
|
|
307
323
|
{ name: 'ELANOUS_SUBSTRATE', value: 'pod' },
|
|
308
324
|
{ name: 'ELANOUS_POD_NAME', valueFrom: { fieldRef: { fieldPath: 'metadata.name' } } },
|
|
325
|
+
...(provenance?.env ?? []),
|
|
309
326
|
...(tmpfs ? [{ name: 'TMPDIR', value: POD_TMPFS_MOUNT_PATH }] : []),
|
|
310
327
|
],
|
|
311
328
|
...(secretKeys || o.hostMirror || hostDirMounts.length || tmpfs ? { volumeMounts: [
|
|
@@ -382,6 +399,9 @@ export interface PodCommandResult {
|
|
|
382
399
|
}
|
|
383
400
|
|
|
384
401
|
export interface RunPodCommandOptions {
|
|
402
|
+
provenance?: Partial<PodProvenance>;
|
|
403
|
+
/** Release gate keeps availability: missing required identity becomes unknown and emits a warning. */
|
|
404
|
+
provenanceGate?: boolean;
|
|
385
405
|
command: readonly string[];
|
|
386
406
|
/** pod run opt-in; other runPodCommand callers keep their existing Job script. */
|
|
387
407
|
returnLogs?: boolean;
|
|
@@ -434,6 +454,9 @@ export interface RunPodCommandOptions {
|
|
|
434
454
|
checkPool?: typeof checkPodPool;
|
|
435
455
|
syncImages?: typeof syncPoolImages;
|
|
436
456
|
imageCommit?: string | null;
|
|
457
|
+
/** Shelf: require this exact registry tag; never fall back to a mutable local image. */
|
|
458
|
+
shelfImage?: string;
|
|
459
|
+
shelfLookup?: typeof registryImageRef;
|
|
437
460
|
readSkillEnv?: (skills: readonly string[]) => Record<string, string>;
|
|
438
461
|
grokCredentials?: () => { grokAuth?: string; grokApiKey?: string; ghToken: string };
|
|
439
462
|
artifactsRoot?: string;
|
|
@@ -510,6 +533,9 @@ function awaitPodCommandAbort<T>(pending: Promise<T>, signal?: AbortSignal): Pro
|
|
|
510
533
|
|
|
511
534
|
/** 풀 자리 · 이미지 판 · Job 적용 · 완료 대기 · 산출 회수 · Secret 정리. */
|
|
512
535
|
export async function runPodCommand(options: RunPodCommandOptions): Promise<PodCommandResult> {
|
|
536
|
+
// A non-gate Job must have its owner before a pool lease, Secret, or Job is created.
|
|
537
|
+
assertGateProvenanceException(options.provenanceGate, options.provenance, options.clone, options.source);
|
|
538
|
+
const provenance = podProvenanceMetadata(options.provenance, options.provenanceGate);
|
|
513
539
|
const nowMs = options.nowMs ?? Date.now;
|
|
514
540
|
const ensureLive = () => {
|
|
515
541
|
if (options.signal?.aborted) throw options.signal.reason ?? new Error('pod command aborted');
|
|
@@ -521,11 +547,15 @@ export async function runPodCommand(options: RunPodCommandOptions): Promise<PodC
|
|
|
521
547
|
const skills = [...new Set((options.skills ?? []).map((s) => s.trim()).filter(Boolean))];
|
|
522
548
|
const llm = options.llm;
|
|
523
549
|
const log = options.log ?? ((c, e, d) => debug.log(c, e, d));
|
|
550
|
+
if (provenance.missing.length) log('pod.provenance', 'missing-gate-fields', { missing: provenance.missing });
|
|
524
551
|
const baseKubectl = options.kubectl ?? ((args: readonly string[], input?: string) =>
|
|
525
552
|
defaultKubectl(args, input, options.signal ? POD_COMMAND_CANCEL_KUBECTL_TIMEOUT_MS : undefined));
|
|
526
553
|
const sleep = options.sleep ?? ((ms: number) => new Promise<void>((r) => setTimeout(r, ms)));
|
|
527
554
|
const namespace = options.namespace ?? 'elanous-test';
|
|
528
555
|
const image = options.image ?? podCommandImageFor(skills, options.clone === true, options.lite === true);
|
|
556
|
+
if (options.shelfImage && (options.image !== options.shelfImage || !options.imageCommit || !options.clone || options.source?.kind !== 'commit' || options.source.sha !== options.imageCommit)) {
|
|
557
|
+
throw new Error('pod shelf: image and source commit must match');
|
|
558
|
+
}
|
|
529
559
|
const memoryLimit = options.memoryLimit ?? (image === POD_LITE_IMAGE ? POD_LITE_MEMORY_LIMIT : undefined);
|
|
530
560
|
const repoUrl = options.repoUrl ?? 'https://github.com/ElanvitalAI/elanous';
|
|
531
561
|
const deadlineSeconds = options.deadlineSeconds ?? POD_COMMAND_DEADLINE_SECONDS;
|
|
@@ -579,6 +609,7 @@ export async function runPodCommand(options: RunPodCommandOptions): Promise<PodC
|
|
|
579
609
|
if (member && pool) pool.release(member);
|
|
580
610
|
ensureLive();
|
|
581
611
|
}
|
|
612
|
+
if (options.shelfImage && !member) throw new Error('pod shelf: remote pool member required');
|
|
582
613
|
const currentContext = member || options.kubectl ? null : baseKubectl(['config', 'current-context']);
|
|
583
614
|
const context = member?.context ?? (currentContext?.status === 0 ? currentContext.stdout.trim() : '');
|
|
584
615
|
const kubectlRaw: Kubectl = (args, stdin) => baseKubectl(context ? ['--context', context, ...args] : [...args], stdin);
|
|
@@ -617,10 +648,16 @@ export async function runPodCommand(options: RunPodCommandOptions): Promise<PodC
|
|
|
617
648
|
const localSkillsDigest = options.kubectl ? null : image === POD_LITE_IMAGE
|
|
618
649
|
? podSkillsDigest(POD_NETWORK_SKILLS, resolve(import.meta.dir, '../../../skills')).digest
|
|
619
650
|
: podSkillsDigest(resolvePodSkills(env).skills).digest;
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
651
|
+
if (options.shelfImage) {
|
|
652
|
+
const found = (options.shelfLookup ?? registryImageRef)(member, imageCommit);
|
|
653
|
+
if (found !== options.shelfImage) throw new Error(`pod shelf: selected image unavailable on ${member.context}`);
|
|
654
|
+
member = { ...member, imageRef: found };
|
|
655
|
+
} else {
|
|
656
|
+
const syncs = await awaitPodCommandAbort((options.syncImages ?? syncPoolImages)([member], image, imageCommit, { localSkillsDigest }), options.signal);
|
|
657
|
+
const synced = syncs.get(member.context);
|
|
658
|
+
if (synced && !synced.ok) throw new Error(`pod command: 이미지 판을 못 맞췄다: ${synced.detail}`);
|
|
659
|
+
if (synced?.imageRef) member = { ...member, imageRef: synced.imageRef };
|
|
660
|
+
}
|
|
624
661
|
}
|
|
625
662
|
const imageRef = member?.imageRef;
|
|
626
663
|
if (!imageRef) log('pod.command-job', 'image-fallback-local', { reason: !member ? 'no-pool-member' : !imageCommit ? 'image-commit-unavailable' : 'registry-tag-unavailable' });
|
|
@@ -661,6 +698,7 @@ export async function runPodCommand(options: RunPodCommandOptions): Promise<PodC
|
|
|
661
698
|
}
|
|
662
699
|
const manifest = podCommandJobManifest({
|
|
663
700
|
name, namespace, launch, image: imageRef ?? image,
|
|
701
|
+
provenance: options.provenance, provenanceGate: options.provenanceGate,
|
|
664
702
|
...(imageRef ? { imagePullPolicy: 'IfNotPresent' as const } : {}),
|
|
665
703
|
repoUrl, command, skills, ...(options.returnLogs ? { returnLogs: true } : {}), ...(llm ? { llm } : {}), ...(options.clone ? { clone: true } : {}),
|
|
666
704
|
...(options.source ? { source: options.source } : {}), ...(hostMirror ? { hostMirror } : {}), ...(bunCache ? { bunCache } : {}), ...(installSlots ? { installSlots } : {}), ...(memoryLimit ? { memoryLimit } : {}), ...(options.cpu ? { cpu: options.cpu } : {}), ...(options.tmpfsSizeLimit ? { tmpfsSizeLimit: options.tmpfsSizeLimit } : {}), deadlineSeconds,
|
|
@@ -14,9 +14,12 @@
|
|
|
14
14
|
* 이미지는 표준판(elanous-harness)만 — 이 발사 경로는 lite 를 쓰지 않는다.
|
|
15
15
|
*/
|
|
16
16
|
import { spawnSync } from 'node:child_process';
|
|
17
|
+
import { mkdtempSync, rmSync } from 'node:fs';
|
|
18
|
+
import { tmpdir } from 'node:os';
|
|
19
|
+
import { join } from 'node:path';
|
|
17
20
|
import { SPAWN_SYNC_MAX_BUFFER } from '../../util/spawn-sync-output.js';
|
|
18
21
|
import { debug } from '../../debug/log.js';
|
|
19
|
-
import { memberHasRegistry, registryImageRef, type RemoteRun } from './pod-pool.js';
|
|
22
|
+
import { memberHasRegistry, registryImageRef, syncPoolImages, parsePodPool, type PodPoolMember, type RemoteRun } from './pod-pool.js';
|
|
20
23
|
|
|
21
24
|
const POD_IMAGE = 'elanous-harness';
|
|
22
25
|
|
|
@@ -59,6 +62,91 @@ export function resolvePodImageSource(
|
|
|
59
62
|
return { kind: 'pinned', ref: podImageRef(registry, commit), commit, registry };
|
|
60
63
|
}
|
|
61
64
|
|
|
65
|
+
export interface PodShelfImage { readonly commit: string; readonly image: string; readonly action: 'fresh' | 'built' }
|
|
66
|
+
|
|
67
|
+
export function resolvePodShelfRef(ref: string, cwd = process.cwd(), exec = spawnSync): string {
|
|
68
|
+
const requested = ref.trim();
|
|
69
|
+
if (!requested || requested.startsWith('-') || /[\s\x00-\x1f]/u.test(requested)) throw new Error('pod shelf: invalid ref');
|
|
70
|
+
const spec = /^#?(\d+)$/u.exec(requested);
|
|
71
|
+
if (spec) {
|
|
72
|
+
const pr = exec('gh', ['pr', 'view', spec[1]!, '--json', 'headRefOid', '--jq', '.headRefOid'], { cwd, encoding: 'utf8', timeout: 30_000 });
|
|
73
|
+
const sha = String(pr.stdout ?? '').trim().toLowerCase();
|
|
74
|
+
if (pr.status !== 0 || !/^[0-9a-f]{40}$/u.test(sha)) throw new Error(`pod shelf: PR head unavailable: ${requested}`);
|
|
75
|
+
return sha;
|
|
76
|
+
}
|
|
77
|
+
const git = exec('git', ['rev-parse', '--verify', `${requested}^{commit}`], { cwd, encoding: 'utf8', timeout: 30_000 });
|
|
78
|
+
const sha = String(git.stdout ?? '').trim().toLowerCase();
|
|
79
|
+
if (git.status !== 0 || !/^[0-9a-f]{40}$/u.test(sha)) throw new Error(`pod shelf: ref unavailable: ${requested}`);
|
|
80
|
+
return sha;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Checkout only the requested commit; syncPoolImages retains its per-host build lock and SHA12 registry check. */
|
|
84
|
+
export async function putPodShelfImage(ref: string, pool: string, deps: {
|
|
85
|
+
resolve?: (ref: string) => string;
|
|
86
|
+
sync?: typeof syncPoolImages;
|
|
87
|
+
checkout?: (commit: string, dir: string) => void;
|
|
88
|
+
tempDir?: () => string;
|
|
89
|
+
remove?: (dir: string) => void;
|
|
90
|
+
hasRegistry?: (member: PodPoolMember) => boolean;
|
|
91
|
+
} = {}): Promise<PodShelfImage> {
|
|
92
|
+
const members = parsePodPool(pool);
|
|
93
|
+
if (members.length !== 1 || !members[0]!.sshHost) throw new Error('pod shelf: one remote registry pool member is required');
|
|
94
|
+
const member: PodPoolMember = members[0]!;
|
|
95
|
+
if (!(deps.hasRegistry ?? memberHasRegistry)(member)) throw new Error('pod shelf: remote registry unavailable');
|
|
96
|
+
const commit = (deps.resolve ?? resolvePodShelfRef)(ref);
|
|
97
|
+
if (!/^[0-9a-f]{40}$/u.test(commit)) throw new Error('pod shelf: full commit required');
|
|
98
|
+
const registry = member.registry ?? podImageRegistry();
|
|
99
|
+
const image = podImageRef(registry, commit);
|
|
100
|
+
if (!deps.sync && registryImageRef(member, commit) === image) return { commit, image, action: 'fresh' };
|
|
101
|
+
const dir = (deps.tempDir ?? (() => mkdtempSync(join(tmpdir(), 'pod-shelf-'))))();
|
|
102
|
+
try {
|
|
103
|
+
if (deps.checkout) deps.checkout(commit, dir);
|
|
104
|
+
else {
|
|
105
|
+
const clone = spawnSync('git', ['clone', '-q', '--no-checkout', process.cwd(), dir], { encoding: 'utf8', timeout: 120_000, maxBuffer: SPAWN_SYNC_MAX_BUFFER });
|
|
106
|
+
if (clone.status !== 0) throw new Error(`pod shelf: clone failed: ${clone.stderr}`);
|
|
107
|
+
const fetch = spawnSync('git', ['-C', dir, 'fetch', '-q', 'origin', commit], { encoding: 'utf8', timeout: 120_000, maxBuffer: SPAWN_SYNC_MAX_BUFFER });
|
|
108
|
+
if (fetch.status !== 0) {
|
|
109
|
+
const remote = spawnSync('git', ['remote', 'get-url', 'origin'], { encoding: 'utf8', timeout: 10_000, maxBuffer: SPAWN_SYNC_MAX_BUFFER });
|
|
110
|
+
if (remote.status !== 0) throw new Error(`pod shelf: fetch failed: ${fetch.stderr}`);
|
|
111
|
+
const fromOrigin = spawnSync('git', ['-C', dir, 'fetch', '-q', String(remote.stdout).trim(), commit], { encoding: 'utf8', timeout: 120_000, maxBuffer: SPAWN_SYNC_MAX_BUFFER });
|
|
112
|
+
if (fromOrigin.status !== 0) throw new Error(`pod shelf: fetch failed: ${fromOrigin.stderr}`);
|
|
113
|
+
}
|
|
114
|
+
const checkout = spawnSync('git', ['-C', dir, 'checkout', '--detach', commit], { encoding: 'utf8', timeout: 120_000, maxBuffer: SPAWN_SYNC_MAX_BUFFER });
|
|
115
|
+
if (checkout.status !== 0) throw new Error(`pod shelf: checkout failed: ${checkout.stderr}`);
|
|
116
|
+
}
|
|
117
|
+
const sync = deps.sync ?? syncPoolImages;
|
|
118
|
+
const results = await sync([member], 'elanous-harness:local', commit, {
|
|
119
|
+
buildScript: join(dir, 'docker/harness/build.sh'),
|
|
120
|
+
...(deps.sync ? {} : { ship: () => ({ ok: false, action: 'failed' as const, detail: 'shelf build failed; local image fallback prohibited' }) }),
|
|
121
|
+
remoteBuild: deps.sync ? undefined : async (host, cluster, registry) => {
|
|
122
|
+
const { spawn } = await import('node:child_process');
|
|
123
|
+
return new Promise((done) => {
|
|
124
|
+
const child = spawn('bash', [join(dir, 'docker/harness/build.sh')], { env: { ...process.env, ELANOUS_BUILD_REMOTE: host, ELANOUS_L2_CLUSTER: cluster, ...(registry ? { ELANOUS_BUILD_REGISTRY: registry } : {}) }, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
125
|
+
let output = '';
|
|
126
|
+
child.stdout.on('data', (chunk) => { output += chunk; });
|
|
127
|
+
child.stderr.on('data', (chunk) => { output += chunk; });
|
|
128
|
+
child.on('close', (code) => done({ ok: code === 0, detail: output.slice(-300) }));
|
|
129
|
+
child.on('error', (error) => done({ ok: false, detail: error.message }));
|
|
130
|
+
});
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
const result = results.get(member.context);
|
|
134
|
+
if (!result?.ok || result.imageRef !== image) throw new Error(`pod shelf: image unavailable: ${result?.detail ?? image}`);
|
|
135
|
+
return { commit, image, action: result.action === 'fresh' ? 'fresh' : 'built' };
|
|
136
|
+
} finally { (deps.remove ?? ((path) => rmSync(path, { recursive: true, force: true })))(dir); }
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function listPodShelfImages(pool: string, run: RemoteRun): string[] {
|
|
140
|
+
const member = parsePodPool(pool)[0];
|
|
141
|
+
if (!member?.sshHost) throw new Error('pod shelf: remote pool member required');
|
|
142
|
+
const registry = member.registry ?? podImageRegistry();
|
|
143
|
+
const port = registry.slice(registry.lastIndexOf(':') + 1);
|
|
144
|
+
const result = run(member.sshHost, `curl -fsS --max-time 5 http://localhost:${port}/v2/elanous-harness/tags/list 2>/dev/null`);
|
|
145
|
+
if (result.status !== 0) throw new Error(`pod shelf: registry unavailable: ${result.stderr}`);
|
|
146
|
+
const tags: unknown = JSON.parse(result.stdout).tags;
|
|
147
|
+
return Array.isArray(tags) ? tags.filter((tag): tag is string => typeof tag === 'string' && /^[0-9a-f]{12}$/u.test(tag)).map((tag) => `${registry}/elanous-harness:${tag}`) : [];
|
|
148
|
+
}
|
|
149
|
+
|
|
62
150
|
export function podImageMissingMessage(ref: string): string {
|
|
63
151
|
return `기준 이미지 ${ref} 가 레지스트리에 없다 — 레일이 굽기 전이다`;
|
|
64
152
|
}
|
|
@@ -236,6 +236,10 @@ export interface ExternalGitHubProbe {
|
|
|
236
236
|
githubHttps: boolean | null;
|
|
237
237
|
measuredAt: string | null;
|
|
238
238
|
reason: string | null;
|
|
239
|
+
/** Name of the disposable Pod for matching this reading with its Kubernetes logs. */
|
|
240
|
+
podName?: string;
|
|
241
|
+
/** Unmodified stdout from `kubectl logs` for this Pod (only present when readable). */
|
|
242
|
+
podLog?: string;
|
|
239
243
|
}
|
|
240
244
|
const DNS_CACHE_MS = 10 * 60_000;
|
|
241
245
|
const DNS_STALE_OK_MS = 30 * 60_000;
|
|
@@ -261,9 +265,11 @@ export function probePoolGitHub(context: string, kubectl: PoolKubectl = leaseKub
|
|
|
261
265
|
if (typeof record?.at !== 'number' || now() < record.at || now() - record.at >= 60_000) return null;
|
|
262
266
|
if (typeof record.measuredAt !== 'string' || ![true, false].includes(record.externalDns as boolean) ||
|
|
263
267
|
![true, false, null].includes(record.githubHttps as boolean | null) ||
|
|
264
|
-
!(record.reason === null || typeof record.reason === 'string')
|
|
268
|
+
!(record.reason === null || typeof record.reason === 'string') ||
|
|
269
|
+
typeof record.podName !== 'string' || typeof record.podLog !== 'string') return null;
|
|
265
270
|
return { externalDns: record.externalDns as boolean, githubHttps: record.githubHttps as boolean | null,
|
|
266
|
-
measuredAt: record.measuredAt, reason: record.reason as string | null
|
|
271
|
+
measuredAt: record.measuredAt, reason: record.reason as string | null,
|
|
272
|
+
podName: record.podName, podLog: record.podLog };
|
|
267
273
|
} catch { return null; }
|
|
268
274
|
};
|
|
269
275
|
const fresh = cached();
|
|
@@ -285,6 +291,7 @@ export function probePoolGitHub(context: string, kubectl: PoolKubectl = leaseKub
|
|
|
285
291
|
const shared = cached();
|
|
286
292
|
if (shared) return shared;
|
|
287
293
|
const result = runPoolGitHubProbe(context, kubectl, new Date(now()).toISOString());
|
|
294
|
+
debug.log('pod.lease', 'github-egress-probe', { context, ...result });
|
|
288
295
|
if (result.measuredAt !== null) {
|
|
289
296
|
const temp = `${cache}.${randomUUID()}.tmp`;
|
|
290
297
|
try { writeFileSync(temp, JSON.stringify({ ...result, at: now() }), { mode: 0o600 }); renameSync(temp, cache); }
|
|
@@ -300,16 +307,18 @@ export function probePoolGitHub(context: string, kubectl: PoolKubectl = leaseKub
|
|
|
300
307
|
function runPoolGitHubProbe(context: string, kubectl: PoolKubectl, measuredAt: string): ExternalGitHubProbe {
|
|
301
308
|
const name = `elanous-egress-${randomUUID().slice(0, 12)}`;
|
|
302
309
|
const base = ['--context', context, '--request-timeout=15s'];
|
|
303
|
-
const unknown = (reason: string): ExternalGitHubProbe => ({ externalDns: null, githubHttps: null, measuredAt: null, reason });
|
|
304
310
|
let created = false;
|
|
311
|
+
const unknown = (reason: string): ExternalGitHubProbe => ({ externalDns: null, githubHttps: null, measuredAt: null, reason,
|
|
312
|
+
...(created ? { podName: name } : {}) });
|
|
305
313
|
try {
|
|
306
314
|
const manifest = JSON.stringify({ apiVersion: 'v1', kind: 'Pod', metadata: { name, namespace: 'elanous-test', labels: { 'elanous.probe': 'github-egress' } }, spec: {
|
|
307
315
|
restartPolicy: 'Never', activeDeadlineSeconds: 35, automountServiceAccountToken: false,
|
|
308
316
|
containers: [{ name: 'probe', image: 'busybox:1.36', command: ['sh', '-c',
|
|
309
|
-
'if nslookup github.com
|
|
317
|
+
'if nslookup github.com 2>&1; then echo EXTERNAL_DNS_OK; if wget -T 8 -O /dev/null https://github.com/ 2>&1; then echo GITHUB_HTTPS_OK; else echo GITHUB_HTTPS_FAILED; fi; else echo EXTERNAL_DNS_FAILED; fi'],
|
|
310
318
|
resources: { requests: { cpu: '0', memory: '16Mi' }, limits: { cpu: '100m', memory: '64Mi' } } }],
|
|
311
319
|
} });
|
|
312
|
-
|
|
320
|
+
const createdPod = kubectl([...base, 'create', '-f', '-'], manifest);
|
|
321
|
+
if (createdPod.status !== 0) return unknown(`probe create failed${createdPod.stderr.trim() ? `: ${createdPod.stderr.trim().split('\n').pop()!.slice(0, 160)}` : ''}`);
|
|
313
322
|
created = true;
|
|
314
323
|
const waited = kubectl([...base, '-n', 'elanous-test', 'wait', '--for=jsonpath={.status.phase}=Succeeded', `pod/${name}`, '--timeout=25s']);
|
|
315
324
|
const podResult = kubectl([...base, '-n', 'elanous-test', 'get', `pod/${name}`, '-o', 'json']);
|
|
@@ -327,10 +336,10 @@ function runPoolGitHubProbe(context: string, kubectl: PoolKubectl, measuredAt: s
|
|
|
327
336
|
const httpsOk = markers.includes('GITHUB_HTTPS_OK');
|
|
328
337
|
const httpsFail = markers.includes('GITHUB_HTTPS_FAILED');
|
|
329
338
|
if (dnsFail && !dnsOk && !httpsOk && !httpsFail)
|
|
330
|
-
return { externalDns: false, githubHttps: null, measuredAt, reason: 'external DNS failed' };
|
|
339
|
+
return { externalDns: false, githubHttps: null, measuredAt, reason: 'external DNS failed', podName: name, podLog: logs.stdout };
|
|
331
340
|
if (dnsOk && !dnsFail && httpsOk !== httpsFail)
|
|
332
|
-
return { externalDns: true, githubHttps: httpsOk, measuredAt, reason: httpsFail ? 'GitHub HTTPS failed' : null };
|
|
333
|
-
return unknown(waited.status === 0 ? 'probe output unavailable' : 'probe timeout');
|
|
341
|
+
return { externalDns: true, githubHttps: httpsOk, measuredAt, reason: httpsFail ? 'GitHub HTTPS failed' : null, podName: name, podLog: logs.stdout };
|
|
342
|
+
return { ...unknown(waited.status === 0 ? 'probe output unavailable' : 'probe timeout'), podLog: logs.stdout };
|
|
334
343
|
} catch { return unknown('probe failed'); }
|
|
335
344
|
finally {
|
|
336
345
|
if (created) try { kubectl([...base, '-n', 'elanous-test', 'delete', `pod/${name}`, '--ignore-not-found=true', '--wait=false']); }
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { BUDGET_SNAPSHOT_ENV } from '../../execution-loop/launch-gate.js';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
/** The Pod push fence embeds a different six-hex run key in each independent launch. */
|
|
4
|
+
export function stablePodRunGuard(text: string): string {
|
|
5
|
+
return text.replace(/pr-create-r[0-9a-f]{6}-/g, 'pr-create-r<run>-')
|
|
6
|
+
.replace(/-r[0-9a-f]{6}(?=[-|*])/g, '-r<run>');
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// Preserve the manifest contract except for the budget timestamp and run identities generated per launch.
|
|
4
10
|
export function stablePodManifestText(applied: Array<Record<string, any>>): string {
|
|
5
11
|
const manifests = structuredClone(applied.filter((entry) => entry.kind === 'Job' || entry.kind === 'Secret'));
|
|
6
12
|
let snapshots = 0;
|
|
@@ -19,5 +25,5 @@ export function stablePodManifestText(applied: Array<Record<string, any>>): stri
|
|
|
19
25
|
}
|
|
20
26
|
}
|
|
21
27
|
if (snapshots === 0) throw new Error('manifest comparison requires a budget snapshot');
|
|
22
|
-
return JSON.stringify(manifests).replace(/run-[0-9a-f-]{36}/g, 'run-<id>');
|
|
28
|
+
return stablePodRunGuard(JSON.stringify(manifests).replace(/run-[0-9a-f-]{36}/g, 'run-<id>'));
|
|
23
29
|
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { redactSecretText } from '../../debug/log.js';
|
|
2
|
+
|
|
3
|
+
/** The work that owns a Pod. Job metadata is the current view; pod.provenance events retain history. */
|
|
4
|
+
export interface PodProvenance {
|
|
5
|
+
actorKind: string;
|
|
6
|
+
actor: string;
|
|
7
|
+
kind: string;
|
|
8
|
+
run: string;
|
|
9
|
+
phase: string;
|
|
10
|
+
requestedAt: string;
|
|
11
|
+
character?: string;
|
|
12
|
+
launchHost?: string;
|
|
13
|
+
entrance?: string;
|
|
14
|
+
graphNode?: string;
|
|
15
|
+
cell?: string;
|
|
16
|
+
version?: string;
|
|
17
|
+
commit?: string;
|
|
18
|
+
shard?: string;
|
|
19
|
+
reason?: string;
|
|
20
|
+
pool?: string;
|
|
21
|
+
cpuReq?: string;
|
|
22
|
+
memReq?: string;
|
|
23
|
+
warmReused?: boolean;
|
|
24
|
+
argv?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const required = ['actorKind', 'actor', 'kind', 'run', 'phase', 'requestedAt'] as const;
|
|
28
|
+
const names: Record<keyof PodProvenance, string> = {
|
|
29
|
+
actorKind: 'actor-kind', actor: 'actor', kind: 'kind', run: 'run', phase: 'phase', requestedAt: 'requested-at',
|
|
30
|
+
character: 'character', launchHost: 'launch-host', entrance: 'entrance', graphNode: 'graph-node', cell: 'cell',
|
|
31
|
+
version: 'version', commit: 'commit', shard: 'shard', reason: 'reason',
|
|
32
|
+
pool: 'pool', cpuReq: 'cpu-req', memReq: 'mem-req',
|
|
33
|
+
warmReused: 'warm-reused', argv: 'argv',
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const labelValue = (value: string): string => {
|
|
37
|
+
const cleaned = value.toLowerCase().replace(/[^a-z0-9._-]/g, '-').replace(/^[^a-z0-9]+|[^a-z0-9]+$/g, '');
|
|
38
|
+
return cleaned.slice(0, 63).replace(/[^a-z0-9]+$/g, '') || 'unknown';
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export interface PodProvenanceMetadata {
|
|
42
|
+
labels: Record<string, string>;
|
|
43
|
+
annotations: Record<string, string>;
|
|
44
|
+
env: Array<{ name: string; value: string }>;
|
|
45
|
+
missing: string[];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** A gate reports missing identity as `unknown`; every other launch refuses it before kubectl apply. */
|
|
49
|
+
export function podProvenanceMetadata(input: Partial<PodProvenance> | undefined, gate = false): PodProvenanceMetadata {
|
|
50
|
+
const missing = required.filter((key) => typeof input?.[key] !== 'string' || !input?.[key]?.trim());
|
|
51
|
+
if (missing.length && !gate) throw new Error(`pod provenance missing: ${missing.map((key) => names[key]).join(', ')}`);
|
|
52
|
+
const values: Record<string, string> = {};
|
|
53
|
+
for (const [key, name] of Object.entries(names) as Array<[keyof PodProvenance, string]>) {
|
|
54
|
+
const raw = input?.[key];
|
|
55
|
+
if (raw === undefined || raw === null || raw === '') continue;
|
|
56
|
+
const value = String(raw).trim();
|
|
57
|
+
if (!value) continue;
|
|
58
|
+
// Metadata and env are observable by many users. Never carry a raw secret through an identity field.
|
|
59
|
+
const redacted = redactSecretText(value);
|
|
60
|
+
if (redacted !== value && key !== 'reason' && key !== 'argv') throw new Error(`pod provenance secret in ${name}`);
|
|
61
|
+
values[name] = redacted;
|
|
62
|
+
}
|
|
63
|
+
for (const key of missing) values[names[key]] = 'unknown';
|
|
64
|
+
// The launch reason is observable even when the caller cannot supply one.
|
|
65
|
+
values.reason ??= 'unknown';
|
|
66
|
+
const labels: Record<string, string> = {};
|
|
67
|
+
const annotations: Record<string, string> = {};
|
|
68
|
+
const env: Array<{ name: string; value: string }> = [];
|
|
69
|
+
for (const [name, value] of Object.entries(values)) {
|
|
70
|
+
const label = name === 'run' || name === 'commit' ? value.slice(0, 8) : value;
|
|
71
|
+
if (name !== 'reason' && name !== 'argv') labels[`elanous.ai/${name}`] = labelValue(label);
|
|
72
|
+
annotations[`elanous.ai/${name}`] = value;
|
|
73
|
+
env.push({ name: `ELANOUS_POD_${name.toUpperCase().replaceAll('-', '_')}`, value });
|
|
74
|
+
}
|
|
75
|
+
return { labels, annotations, env, missing: missing.map((key) => names[key]) };
|
|
76
|
+
}
|
|
@@ -179,6 +179,24 @@ export function podSourceScript(source: PodSource, repoUrl: string, mirrorPath =
|
|
|
179
179
|
'if ! [[ "$pr_sha" =~ ^[0-9a-f]{40}$ ]]; then exit 5; fi',
|
|
180
180
|
`if ! git cat-file -e "$pr_sha^{commit}" 2>/dev/null; then git fetch --depth 50 origin pull/${n}/head || exit 5; fi`,
|
|
181
181
|
`git checkout -B pr-${n} "$pr_sha" || exit 5`,
|
|
182
|
+
// A pinned image starts at one shallow main commit. Fetching a PR tip at
|
|
183
|
+
// depth 50 can leave that pin and the PR in disconnected shallow spans.
|
|
184
|
+
// Deepen only after merge-base fails; the gate still reports unavailable
|
|
185
|
+
// if ancestry cannot be obtained.
|
|
186
|
+
'remote_info=$(git ls-remote --symref origin HEAD 2>/dev/null) || { printf \'ELANOUS_POD_COMPARISON_BASE unavailable\\n\'; exit 5; }',
|
|
187
|
+
`base_branch=$(printf '%s\\n' "$remote_info" | awk '$1=="ref:" && $3=="HEAD" && $2 ~ /^refs\\/heads\\// {sub(/^refs\\/heads\\//, "", $2); print $2}')`,
|
|
188
|
+
'if [ -n "$base_branch" ] && git check-ref-format --branch "$base_branch" >/dev/null 2>&1; then',
|
|
189
|
+
' base_ref="refs/remotes/origin/$base_branch"',
|
|
190
|
+
' git fetch --depth 50 origin "+refs/heads/$base_branch:$base_ref" || { printf \'ELANOUS_POD_COMPARISON_BASE unavailable\\n\'; exit 5; }',
|
|
191
|
+
' git symbolic-ref refs/remotes/origin/HEAD "$base_ref" || exit 5',
|
|
192
|
+
' if ! git merge-base HEAD "$base_ref" >/dev/null 2>&1 && [ "$(git rev-parse --is-shallow-repository)" = true ]; then',
|
|
193
|
+
` git fetch --deepen=100 origin "+refs/heads/$base_branch:$base_ref" +refs/pull/${n}/head:refs/remotes/origin/pr-${n}-head || printf 'ELANOUS_POD_COMPARISON_BASE deepen-failed\\n'`,
|
|
194
|
+
' fi',
|
|
195
|
+
' if ! git merge-base HEAD "$base_ref" >/dev/null 2>&1 && [ "$(git rev-parse --is-shallow-repository)" = true ]; then',
|
|
196
|
+
` git fetch --unshallow origin "+refs/heads/$base_branch:$base_ref" +refs/pull/${n}/head:refs/remotes/origin/pr-${n}-head || { printf 'ELANOUS_POD_COMPARISON_BASE unavailable\\n'; exit 5; }`,
|
|
197
|
+
' fi',
|
|
198
|
+
" if ! git merge-base HEAD \"$base_ref\" >/dev/null 2>&1; then printf 'ELANOUS_POD_COMPARISON_BASE unavailable\\n'; fi",
|
|
199
|
+
"else printf 'ELANOUS_POD_COMPARISON_BASE unavailable\\n'; exit 5; fi",
|
|
182
200
|
'head=$(git rev-parse HEAD)',
|
|
183
201
|
"printf 'ELANOUS_POD_SOURCE pr %s\\n' \"$head\"",
|
|
184
202
|
].join('\n');
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import type { Kubectl } from './self-implement-pod.js';
|
|
3
|
+
import { podProvenanceMetadata } from './pod-provenance.js';
|
|
4
|
+
import type { WarmWorkerCommand } from './pod-warm-worker-protocol.js';
|
|
5
|
+
import type { WarmPodSpec } from './pod-warm-worker-pod.js';
|
|
6
|
+
|
|
7
|
+
const PREFIX = 'elanous.ai/';
|
|
8
|
+
const STATIC = new Set(['pool', 'image-digest', 'launch-host']);
|
|
9
|
+
const workKey = (key: string) => key.startsWith(PREFIX) && !STATIC.has(key.slice(PREFIX.length));
|
|
10
|
+
|
|
11
|
+
export function warmLeaseMetadata(command: WarmWorkerCommand) {
|
|
12
|
+
return podProvenanceMetadata({ actorKind: 'loop', actor: 'release-gate', character: 'gate-warm-worker',
|
|
13
|
+
entrance: 'release-gate-warm', kind: 'warm', run: command.runId, phase: 'release-gate',
|
|
14
|
+
requestedAt: command.requestedAt || 'unknown', reason: 'gate shadow replay', commit: command.cutSha,
|
|
15
|
+
shard: command.attemptId, warmReused: true });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function warmExecEnv(command: WarmWorkerCommand): string[] {
|
|
19
|
+
return [...warmLeaseMetadata(command).env,
|
|
20
|
+
{ name: 'ELANOUS_POD_LEASE', value: command.leaseId },
|
|
21
|
+
{ name: 'ELANOUS_POD_WORKER', value: command.workerId },
|
|
22
|
+
].map(({ name, value }) => `${name}=${value}`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** One UID/resourceVersion-guarded metadata mutation. A failed patch never admits a lease. */
|
|
26
|
+
export function patchWarmLeaseMetadata(spec: WarmPodSpec, command: WarmWorkerCommand,
|
|
27
|
+
next: 'leased' | 'idle' | 'quarantined', injected?: Kubectl): { ok: boolean; reason: string } {
|
|
28
|
+
const fail = (reason: string) => ({ ok: false, reason });
|
|
29
|
+
if (!command.podUid || !command.requestedAt) return fail('lease-identity-missing');
|
|
30
|
+
const kubectl: Kubectl = injected ?? ((args, input) => {
|
|
31
|
+
const env = { ...process.env };
|
|
32
|
+
for (const name of ['HTTPS_PROXY', 'https_proxy', 'HTTP_PROXY', 'http_proxy', 'ALL_PROXY', 'all_proxy']) delete env[name];
|
|
33
|
+
const result = spawnSync('kubectl', [...args], { encoding: 'utf8', input, env,
|
|
34
|
+
timeout: 120_000, maxBuffer: 2 * 1024 * 1024 });
|
|
35
|
+
return { status: result.status, stdout: result.stdout ?? '', stderr: result.stderr ?? '' };
|
|
36
|
+
});
|
|
37
|
+
const prefix = ['--context', spec.kubectlContext, '-n', spec.namespace];
|
|
38
|
+
const observed = kubectl([...prefix, 'get', 'pod', spec.name, '-o', 'json']);
|
|
39
|
+
if (observed.status !== 0) return fail('pod-read-failed');
|
|
40
|
+
let pod: any;
|
|
41
|
+
try { pod = JSON.parse(observed.stdout); } catch { return fail('pod-read-invalid'); }
|
|
42
|
+
const metadata = pod?.metadata;
|
|
43
|
+
if (metadata?.name !== spec.name || metadata?.namespace !== spec.namespace
|
|
44
|
+
|| metadata?.uid !== command.podUid || typeof metadata?.resourceVersion !== 'string'
|
|
45
|
+
|| !metadata.resourceVersion
|
|
46
|
+
|| metadata.annotations?.['elanous.dev/warm-worker-id'] !== spec.workerId
|
|
47
|
+
|| metadata.annotations?.['elanous.dev/warm-cut-sha'] !== spec.cutSha) return fail('pod-identity-drift');
|
|
48
|
+
const labels = { ...(metadata.labels ?? {}) } as Record<string, string>;
|
|
49
|
+
const annotations = { ...(metadata.annotations ?? {}) } as Record<string, string>;
|
|
50
|
+
if (next === 'leased' && labels[`${PREFIX}state`] !== undefined
|
|
51
|
+
&& labels[`${PREFIX}state`] !== 'idle') return fail('lease-state-not-idle');
|
|
52
|
+
if (next === 'leased' && labels[`${PREFIX}state`] === undefined
|
|
53
|
+
&& [...Object.keys(labels), ...Object.keys(annotations)].some(workKey)) return fail('stale-work-metadata');
|
|
54
|
+
if (next === 'idle' && (labels[`${PREFIX}state`] !== 'leased'
|
|
55
|
+
|| labels[`${PREFIX}lease`] !== command.leaseId)) return fail('lease-state-mismatch');
|
|
56
|
+
if (next === 'quarantined' && labels[`${PREFIX}state`] === 'leased'
|
|
57
|
+
&& labels[`${PREFIX}lease`] !== command.leaseId) return fail('lease-state-mismatch');
|
|
58
|
+
for (const key of Object.keys(labels)) if (workKey(key)) delete labels[key];
|
|
59
|
+
for (const key of Object.keys(annotations)) if (workKey(key)) delete annotations[key];
|
|
60
|
+
if (next === 'leased') {
|
|
61
|
+
const work = warmLeaseMetadata(command);
|
|
62
|
+
Object.assign(labels, work.labels, { [`${PREFIX}state`]: 'leased', [`${PREFIX}lease`]: command.leaseId });
|
|
63
|
+
Object.assign(annotations, work.annotations, { [`${PREFIX}lease`]: command.leaseId });
|
|
64
|
+
} else labels[`${PREFIX}state`] = next;
|
|
65
|
+
const patch = [
|
|
66
|
+
{ op: 'test', path: '/metadata/uid', value: command.podUid },
|
|
67
|
+
{ op: 'test', path: '/metadata/resourceVersion', value: metadata.resourceVersion },
|
|
68
|
+
{ op: 'add', path: '/metadata/labels', value: labels },
|
|
69
|
+
{ op: 'add', path: '/metadata/annotations', value: annotations },
|
|
70
|
+
];
|
|
71
|
+
const updated = kubectl([...prefix, 'patch', 'pod', spec.name, '--type=json', '-p', JSON.stringify(patch), '-o', 'json']);
|
|
72
|
+
if (updated.status !== 0) return fail('conditional-patch-failed');
|
|
73
|
+
try {
|
|
74
|
+
const after = JSON.parse(updated.stdout);
|
|
75
|
+
const matched = after?.metadata?.uid === command.podUid
|
|
76
|
+
&& after?.metadata?.labels?.[`${PREFIX}state`] === next
|
|
77
|
+
&& (next !== 'leased' ? after.metadata.labels[`${PREFIX}lease`] === undefined
|
|
78
|
+
: after.metadata.labels[`${PREFIX}lease`] === command.leaseId);
|
|
79
|
+
return matched ? { ok: true, reason: 'applied' } : fail('patch-result-mismatch');
|
|
80
|
+
} catch { return fail('patch-result-invalid'); }
|
|
81
|
+
}
|
|
@@ -11,6 +11,7 @@ import { elanousStateRoot } from '../../autopilot/state-paths.js';
|
|
|
11
11
|
import { debug } from '../../debug/log.js';
|
|
12
12
|
import { parseWarmWorkerReply, type WarmWorkerCommand, type WarmWorkerKey, type WarmWorkerReply } from './pod-warm-worker-protocol.js';
|
|
13
13
|
import type { WarmWorkerOutcome } from './pod-warm-worker-runtime.js';
|
|
14
|
+
import { warmExecEnv } from './pod-warm-provenance.js';
|
|
14
15
|
|
|
15
16
|
const SHA = /^[a-f0-9]{40}$/;
|
|
16
17
|
const DIGEST = /^sha256:[a-f0-9]{64}$/;
|
|
@@ -424,7 +425,9 @@ export class WarmPodTransport {
|
|
|
424
425
|
try { nodeArch = node.status === 0 ? JSON.parse(node.stdout)?.metadata?.labels?.['kubernetes.io/arch'] : null; }
|
|
425
426
|
catch { nodeArch = null; }
|
|
426
427
|
if (nodeArch !== this.spec.key.architecture) return fallback('pod-unready');
|
|
427
|
-
if (
|
|
428
|
+
if ((command.podUid && uid !== command.podUid) || (this.podUid && uid !== this.podUid)) {
|
|
429
|
+
this.quarantined = true; return fallback('pod-replaced');
|
|
430
|
+
}
|
|
428
431
|
this.podUid = uid;
|
|
429
432
|
const ledgerKey = sha256([this.spec.kubectlContext, this.spec.namespace, this.spec.name, uid].join('\0'));
|
|
430
433
|
const ledgerPath = this.ledgerDir ? join(this.ledgerDir, ledgerKey) : null;
|
|
@@ -434,7 +437,8 @@ export class WarmPodTransport {
|
|
|
434
437
|
this.quarantined = true;
|
|
435
438
|
return fallback('worker-quarantined');
|
|
436
439
|
}
|
|
437
|
-
const exec = this.kubectl([...prefix, 'exec', '-i', this.spec.name, '-c', 'worker', '--',
|
|
440
|
+
const exec = this.kubectl([...prefix, 'exec', '-i', this.spec.name, '-c', 'worker', '--',
|
|
441
|
+
'env', ...warmExecEnv(command), 'bun', '-e', WARM_POD_EXEC], JSON.stringify(command));
|
|
438
442
|
if (exec.status !== 0) { this.quarantined = true; return fallback('exec-failed'); }
|
|
439
443
|
// UID alone is insufficient: ephemeral containers can be added to a live Pod under the same UID.
|
|
440
444
|
const after = this.kubectl([...prefix, 'get', 'pod', this.spec.name, '-o', 'json']);
|
|
@@ -20,6 +20,9 @@ export interface WarmWorkerCommand {
|
|
|
20
20
|
leaseId: string;
|
|
21
21
|
generation: number;
|
|
22
22
|
workerId: string;
|
|
23
|
+
/** Acquired Pod identity and lease timestamp, carried only by the host control path. */
|
|
24
|
+
podUid?: string;
|
|
25
|
+
requestedAt?: string;
|
|
23
26
|
key: WarmWorkerKey;
|
|
24
27
|
deadlineAt: number;
|
|
25
28
|
files: readonly string[];
|