elanous 0.2.10 → 0.2.12
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/-6XrAoD3eU7NyMHAE4xPS/_buildManifest.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1147-466ec15afacc1a65.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1159-0c5ef8bc21cb6afb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1394-ee3795b112907122.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1452.93700483b9d4fc61.js +9 -0
- package/apps/pwa/out/_next/static/chunks/{6062-73e68343a9814f6a.js → 1829-b453a1b22d3999bb.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2176-c379e3ebce874151.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2426.8c6ee0c668d53def.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3170-7ec60a56fa0e4728.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3278-ae6ef084769f6195.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3454-988a53dac60a1763.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3638-aa80b4641ea439db.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3735-fe3402553ac1c9c6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3869-3cd46526879f454c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/390.c373e31b3b9d0c41.js +5 -0
- package/apps/pwa/out/_next/static/chunks/3992-2e522a8ac5f2afde.js +25 -0
- package/apps/pwa/out/_next/static/chunks/4215-c9d91dd644ec1719.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4508-5d0161d1e724d735.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4685-c3c1c625b71accd1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4733-623a79345b171e1c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4883.3bd969ea1d3aaaff.js +2 -0
- package/apps/pwa/out/_next/static/chunks/5532.6899a12958a89b72.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8067-87ce253ec2f23697.js → 5845-e6c003ce8d5fd36a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5978-b27004c677119115.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8046cadf-211ca3c0e00f2b72.js → 5cd7e3a9-404fd6b8d0ecd44c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/6048-4c5c33579bc4ef78.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6303-422dbf0f67c72aac.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6751-bb3595948b2e311b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7274-19b7279f18b47ab3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7277-0b2fc7c16ba2d64b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4323.c5dfb6f503304775.js → 7334.a6679d9168f37aa8.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7551-a0e0707b79164ddd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7758-0b018a9e4fc56200.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7777-79f845045aa1ed86.js +1 -0
- package/apps/pwa/out/_next/static/chunks/79f8be9e-957d68913b590b08.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8025-cc24ee5d8f2fb6f8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8039-df92e08fede5de8f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8206-c6684dcde12be721.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8421-1c4acfb1395a0edf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8605-0259994816604c49.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8725.bf46bb1395dd0fa2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8734-252d511cd276c0dc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8834-ca1cb09c1cb1a6bf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2415.ed6428ff334c7ce2.js → 9269.d2a4795bc6812717.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9538-2816b90923606459.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9656-c1540088f5859c80.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9906-482c6619b3fdda20.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9920-5cfdc5e8fd209825.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-d16993889b125c42.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-cb169620945d1952.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-cefe8169e36cf62a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-d2e35228bc497119.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/{page-54b32bd27726588d.js → page-5c05b0da4bc1392c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-e0ccdb576812b523.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-813c4a52c9d05ca8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-4731f2c215358937.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-a022b257950f39e6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-a2ffc717e04b8b0a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-b59ee4e70fc18fcf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-a67e11ba962337b5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-c172dabd796eb7a3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-d16993889b125c42.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/page-1efd0cb9e0ed9d13.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-d5dc7ff69a37b4cf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-e71c84189e8c683e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-1d6a533ce5a6efcb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-dfca9b7d2de0b135.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-793fa536b3a6ef37.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/page-6dd3bfbbde2a5c2c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-490653e8d4cc8852.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-77447356baad75d2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-29e961191f6057df.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-c822f55b884d9104.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fa4ccfc432424e52.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-6d604227e1664b71.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-9c7e8f744a765daf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-4048543b4b7ad5e0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-4c3a3126abed551a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-64b0aecb1ca75e75.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-4292d0149020e8a7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-db14d0be7f1c460c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-e5d8072cd7cfef73.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-d16993889b125c42.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-9547b5cec7cf76bf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-e6dcb83265530636.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-45248f08d6aecae5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-2cddeb9e2aeedc55.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-e297866051595d81.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/page-baba2e047872fb0f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-e895da4ec9cac345.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-4344faf20216233b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-698bb29f11864b89.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-bd6a6dc1ddc273f2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-2e9e2fb01c9da1c9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-d62ceb13411ebba4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2ea96958-e8c7e112de50b067.js → f48170cc-d05d28bf87a43bf4.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/framework-172630e6aa5e2d7f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-f0fe2d7477845181.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-fc98ac70b44eccb0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-5bd7f10271f72413.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-97d0d85ea05fef9b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/webpack-9e4d02d567b98240.js +1 -0
- package/apps/pwa/out/_next/static/css/128fb7fb670b2fbf.css +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +23 -23
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +23 -23
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +23 -23
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +22 -22
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +22 -22
- package/apps/pwa/out/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/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 -0
- package/apps/pwa/out/inside/index.txt +31 -0
- 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/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 -0
- package/apps/pwa/out/outputs/index.txt +30 -0
- 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 -24
- 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 -24
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +23 -23
- package/graphs/context/nightly-condense.yaml +21 -0
- package/graphs/context/recipes.yaml +12 -0
- package/graphs/demo/inside-seed.yaml +20 -0
- package/graphs/demo/recipes.yaml +12 -0
- package/graphs/drafts/draft-cleanup.yaml +19 -0
- package/graphs/drafts/recipes.yaml +23 -0
- package/graphs/intake/intake-daily.yaml +2 -1
- package/graphs/ops/recipes.yaml +3 -0
- package/graphs/ops/secret-exposure.yaml +24 -0
- package/graphs/release/recipes.yaml +3 -0
- package/graphs/release/release-loop.yaml +4 -1
- package/graphs/video/explainer-line.yaml +2 -2
- package/package.json +2 -1
- package/plugins/card-followup/examples/sample-card.png +0 -0
- package/plugins/elanous-hwp/README.md +25 -0
- package/plugins/elanous-hwp/cli.ts +22 -0
- package/plugins/elanous-hwp/converter.ts +237 -0
- package/plugins/elanous-hwp/fixtures/fixed-controls.hwpx +0 -0
- package/plugins/elanous-hwp/fixtures/fixed-table-controls.hwpx +0 -0
- package/plugins/elanous-hwp/fixtures/fixed.hwpx +0 -0
- package/plugins/elanous-hwp/plugin.json +16 -0
- package/plugins/elanous-hwp/plugin.ts +40 -0
- package/scripts/ci-public-export-leak-gate.ts +75 -14
- package/scripts/ci-public-leak-gate.ts +44 -9
- package/scripts/context-hooks/claude-stop.sh +4 -0
- package/scripts/context-hooks/emit.ts +24 -0
- package/scripts/context-hooks/post-tool-use.sh +4 -0
- package/scripts/coord-channel-watch.sh +12 -0
- package/scripts/coord-requests-bridge.ts +143 -0
- package/scripts/field-feed/lib.ts +4 -0
- package/scripts/fleet/node-setup.sh +20 -3
- package/scripts/homebrew-formula-bump.ts +69 -0
- package/scripts/lib/telegram-inject-args.ts +31 -0
- package/scripts/marketing/measure-site-numbers.ts +169 -0
- package/scripts/measure-gate-shards.ts +309 -0
- package/scripts/measure-pod-memory-by-goal.ts +130 -0
- package/scripts/mission-request-judge.ts +31 -1
- package/scripts/morning-report.ts +2 -2
- package/scripts/opportunity-scan.ts +1 -1
- package/scripts/release-loop/auto-approve-node.ts +21 -4
- package/scripts/release-loop/checklist-gate-node.ts +2 -1
- package/scripts/release-loop/cut-branch.ts +84 -0
- package/scripts/release-loop/gate-node.ts +22 -15
- package/scripts/release-loop/mac-smoke-node.ts +229 -0
- package/scripts/release-loop/node-verdict.ts +3 -3
- package/scripts/release-loop/ops-upgrade-node.ts +47 -6
- package/scripts/release-loop/publish-node.ts +4 -1
- package/scripts/release-loop/tui-node-checks.ts +42 -0
- package/scripts/release-loop/tui-sim-node.ts +32 -8
- package/scripts/release-loop/unattended-release.ts +5 -3
- package/scripts/release-loop/version-node.ts +74 -13
- package/scripts/signal-digest-cycle.ts +2 -7
- package/scripts/signal-digest-photo.ts +11 -0
- package/scripts/tech1-public-measure.ts +94 -0
- package/scripts/telegram-inject.ts +14 -6
- package/scripts/test-diet/node.ts +1 -1
- package/scripts/test-diet/remeasure-list.ts +112 -0
- package/scripts/tui-regress.ts +2 -2
- package/scripts/ux/demo-rehearsal.ts +247 -0
- package/scripts/ux/demo-seed.ts +108 -0
- package/scripts/ux/lib/rehearsal-verdict.ts +80 -0
- package/skills/diagram-master/SKILL.md +366 -0
- package/skills/diagram-master/references/engines/excalidraw/color-palette.md +67 -0
- package/skills/diagram-master/references/engines/excalidraw/element-templates.md +182 -0
- package/skills/diagram-master/references/engines/excalidraw/json-schema.md +71 -0
- package/skills/diagram-master/references/engines/excalidraw/render_excalidraw.py +189 -0
- package/skills/diagram-master/references/engines/excalidraw/render_template.html +57 -0
- package/skills/diagram-master/references/engines/matplotlib/matplotlib-patterns.md +235 -0
- package/skills/diagram-master/references/engines/matplotlib/render_matplotlib.py +184 -0
- package/skills/diagram-master/references/engines/mermaid/domain-templates.md +435 -0
- package/skills/diagram-master/references/engines/mermaid/render_mermaid.py +116 -0
- package/skills/diagram-master/references/engines/mermaid/syntax-reference.md +2247 -0
- package/skills/diagram-master/references/engines/nanobannana/render_nanobannana2.py +168 -0
- package/skills/diagram-master/references/engines/svg/color-palette.md +188 -0
- package/skills/diagram-master/references/engines/svg/html-base-template.html +332 -0
- package/skills/diagram-master/references/engines/svg/interactivity-patterns.md +435 -0
- package/skills/diagram-master/references/engines/svg/render_interactive_svg.py +212 -0
- package/skills/diagram-master/references/engines/svg/svg-element-templates.md +349 -0
- package/skills/diagram-master/references/examples/README.md +69 -0
- package/skills/diagram-master/references/examples/ref_grid_architecture.png +0 -0
- package/skills/diagram-master/references/examples/ref_layered_flow.png +0 -0
- package/skills/diagram-master/references/pyproject.toml +16 -0
- package/skills/diagram-master/references/quality-checklist.md +33 -0
- package/skills/diagram-master/references/routing-guide.md +164 -0
- package/skills/diagram-master/references/uv.lock +1356 -0
- package/skills/google-workspace/SKILL.md +33 -0
- package/skills/google-workspace/scripts/gws-safe.sh +177 -0
- package/skills/grill-me/SKILL.md +1 -1
- package/skills/lecture-note-digitizer/SKILL.md +926 -0
- package/skills/lecture-note-digitizer/references/convert_to_pdf.py +30 -0
- package/skills/lecture-note-digitizer/references/extract_slide_diagram.py +73 -0
- package/skills/lecture-note-digitizer/references/html-template.md +167 -0
- package/skills/lecture-note-digitizer/references/katex-style-guide.md +165 -0
- package/skills/omni-crawl/SKILL.md +21 -11
- package/skills/omni-crawl/scripts/main.ts +6 -6
- package/skills/omni-crawl/src/router.ts +3 -3
- package/skills/omni-digest/SKILL.md +17 -9
- package/skills/omni-digest/digest.md +2 -2
- package/skills/omni-digest/src/fetch.ts +30 -3
- package/skills/pitch-deck/SKILL.md +2 -2
- package/skills/pitch-deck/template.html +2 -2
- package/skills/youtube-master/SKILL.md +163 -0
- package/skills/youtube-master/package-lock.json +558 -0
- package/skills/youtube-master/package.json +13 -0
- package/skills/youtube-master/references/study-note-template.md +124 -0
- package/skills/youtube-master/scripts/main.ts +435 -0
- package/skills/youtube-master/scripts/transcribe-local.ts +24 -0
- package/skills/youtube-master/src/audio.ts +63 -0
- package/skills/youtube-master/src/domain-staging.ts +50 -0
- package/skills/youtube-master/src/env.ts +36 -0
- package/skills/youtube-master/src/obsidian.ts +155 -0
- package/skills/youtube-master/src/router.ts +110 -0
- package/skills/youtube-master/src/stt.ts +199 -0
- package/skills/youtube-master/src/study-note.ts +175 -0
- package/skills/youtube-master/src/summarize.ts +358 -0
- package/skills/youtube-master/src/transcript.ts +76 -0
- package/skills/youtube-master/src/types.ts +91 -0
- package/skills/youtube-master/src/util.ts +93 -0
- package/skills/youtube-master/src/youtube.ts +70 -0
- package/skills/youtube-master/tsconfig.json +13 -0
- package/src/acp/server.ts +5 -2
- package/src/agent-mission/driver.ts +215 -23
- package/src/agent-mission/pty-decision.ts +71 -0
- package/src/agent-mission/review-loop.ts +30 -0
- package/src/autopilot/build/llm-conflict-merge.ts +16 -3
- package/src/autopilot/pty-control-loop.ts +4 -1
- package/src/autopilot/tool-registry.ts +2 -0
- package/src/boot/daemon-prompt-turn.ts +5 -0
- package/src/boot/daemon-runtime.ts +9 -3
- package/src/boot/daemon-tools/web-search.ts +3 -0
- package/src/card-followup/core.ts +109 -0
- package/src/chat/index.ts +2 -0
- package/src/cli/drive-input-jail.ts +101 -0
- package/src/cli/hooks-cli.ts +17 -3
- package/src/cli/make-cli.ts +43 -0
- package/src/cli/persona-cli.ts +98 -9
- package/src/cli/plugin-cli.ts +31 -22
- package/src/cli/pod-cli.ts +36 -9
- package/src/cli/pod-memory-advice.ts +35 -0
- package/src/cli/pr-cli.ts +51 -15
- package/src/cli/pty-drive-cli.ts +41 -7
- package/src/cli/pty-drive-verdict.ts +40 -0
- package/src/cli/release-cli.ts +63 -10
- package/src/cli/setup-cli.ts +43 -1
- package/src/cli/skills-cli.ts +17 -1
- package/src/context-bus/claude-hooks-install.ts +10 -0
- package/src/context-bus/context-now-surfaces.ts +65 -0
- package/src/context-bus/context-now.ts +104 -0
- package/src/context-bus/coord-events.ts +2 -2
- package/src/context-bus/external-events.ts +52 -0
- package/src/context-bus/long-term-memory.ts +108 -0
- package/src/context-bus/nightly-condense.ts +153 -0
- package/src/context-bus/session-events.ts +35 -0
- package/src/dashboard/dashboard-status-lines.ts +2 -0
- package/src/dashboard/index.ts +17 -4
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +31 -1
- package/src/decisions/decision-cards.ts +10 -3
- package/src/decisions/decision-ledger.ts +13 -5
- package/src/discord/sprint21-runtime.ts +5 -1
- package/src/discord-self-message.ts +32 -0
- package/src/discord-slash-wire.ts +5 -1
- package/src/discord.ts +17 -1
- package/src/domains/core-tools.ts +3 -2
- package/src/domains/outbound-alert.ts +105 -32
- package/src/domains/outbound-log-probe.ts +8 -0
- package/src/domains/telegram-kind-route.ts +51 -13
- package/src/exec-requests/default-outputs.ts +25 -0
- package/src/exec-requests/planner.ts +10 -2
- package/src/exec-requests/runner.ts +20 -6
- package/src/exec-requests/store.ts +3 -0
- package/src/execution-loop/launch-gate.ts +109 -0
- package/src/field/field-reel.ts +5 -1
- package/src/field-note/index.ts +201 -0
- package/src/field-note/telegram.ts +142 -0
- package/src/field-note/transcribe.ts +248 -0
- package/src/graph-kinds/registry.ts +3 -3
- package/src/graph-runner/graph-cli.ts +61 -1
- package/src/graph-runner/graph-grow-llm.ts +47 -0
- package/src/graph-runner/graph-grow.ts +50 -0
- package/src/graph-runner/runner.ts +329 -33
- package/src/grok/agent-search.ts +8 -2
- package/src/guardian/secret-exposure-scan.ts +101 -0
- package/src/harness/harness-cli-command.ts +375 -13
- package/src/harness/harness-pod-dispatch.ts +43 -23
- package/src/harness/harness-seams.ts +38 -4
- package/src/harness/harness-stop.ts +23 -4
- package/src/harness/launch-registry.ts +91 -0
- package/src/harness/process-garbage.ts +5 -3
- package/src/hooks/consult-intake.ts +153 -0
- package/src/hooks/dispatch.ts +82 -0
- package/src/hooks/expose.ts +254 -0
- package/src/hooks/github-shadow.ts +108 -0
- package/src/hooks/providers.ts +71 -5
- package/src/hooks/queue.ts +1 -1
- package/src/hooks/receiver.ts +94 -16
- package/src/index.ts +76 -14
- package/src/intake-plane/categorize.ts +18 -46
- package/src/intake-plane/discord-seat-work.ts +38 -2
- package/src/intake-plane/front-route-classifier.ts +33 -39
- package/src/intake-plane/persona-answer.ts +30 -0
- package/src/intake-plane/runtime-callables.ts +8 -1
- package/src/intake-plane/telegram-seat-work.ts +36 -2
- package/src/intake-plane/wish-card.ts +33 -0
- package/src/intake-plane/wish-folder.ts +67 -0
- package/src/intent-gate/gate.ts +30 -10
- package/src/live/detail-switch.ts +5 -0
- package/src/llm/difficulty.ts +57 -0
- package/src/llm/judge-layer.ts +101 -0
- package/src/llm/mission-router.ts +33 -19
- package/src/loops/contract/examples/cmo-seat.loop.yaml +51 -0
- package/src/loops/contract/examples/orchestrator.loop.yaml +57 -0
- package/src/loops/contract/examples/steward.loop.yaml +50 -0
- package/src/loops/contract/loop-agent.schema.json +138 -0
- package/src/loops/contract/validate.ts +64 -0
- package/src/loops/registry.ts +20 -6
- package/src/market/signed-index.ts +4 -0
- package/src/maturity/bot-command-maturity.ts +45 -0
- package/src/maturity/feature-maturity.ts +27 -4
- package/src/model-tier/index.ts +2 -2
- package/src/model-tier/tier-resolver.ts +20 -20
- package/src/model-tier/types.ts +6 -6
- package/src/mss/logging/log-store.ts +15 -0
- package/src/nexus/api/config-model-tier.ts +5 -4
- package/src/nexus/api/fabric-plans.ts +53 -0
- package/src/nexus/api/harness-api.ts +5 -1
- package/src/nexus/api/health.ts +2 -0
- package/src/nexus/api/http-server.ts +67 -8
- package/src/nexus/api/inside-events.ts +226 -0
- package/src/nexus/api/meta-api.ts +21 -8
- package/src/nexus/api/missions.ts +6 -1
- package/src/nexus/api/ops-seats.ts +33 -0
- package/src/nexus/api/outbound-report.ts +40 -7
- package/src/nexus/api/outputs-route.ts +44 -0
- package/src/nexus/api/personas.ts +95 -53
- package/src/nexus/api/seat-requests.ts +44 -2
- package/src/nexus/api/sessions-store.ts +17 -38
- package/src/nexus/api/sub-seat-charters.ts +34 -0
- package/src/nexus/api/tabs-mutations.ts +3 -1
- package/src/nexus/api/task-cards-api.ts +19 -0
- package/src/nexus/api/trace.ts +18 -6
- package/src/nexus/api/workflow-history.ts +88 -0
- package/src/nexus/api/workflow-run-event-bridge.ts +1 -0
- package/src/nexus/api/workflows.ts +132 -6
- package/src/nexus/index.ts +2 -0
- package/src/nexus/kinds/webterm.ts +2 -1
- package/src/nexus/outbound/delivery-ledger.ts +17 -8
- package/src/nexus/outbound/router.ts +8 -3
- package/src/nexus/webterm/pty.ts +6 -2
- package/src/onboarding/cli-auth-status.ts +80 -0
- package/src/onboarding/voice-ai.ts +3 -3
- package/src/org/seat-budget.ts +48 -0
- package/src/org/stall-escalation.ts +63 -0
- package/src/outputs/ledger.ts +120 -0
- package/src/persona/persona-todo.ts +79 -0
- package/src/persona/presets.ts +123 -2
- package/src/persona/team-set.ts +155 -0
- package/src/plugins/install/plugin-install.ts +14 -0
- package/src/plugins/maker/market-bundle.ts +110 -0
- package/src/plugins/maker/plugin-maker.ts +254 -11
- package/src/plugins/maker/wizard-generate.ts +103 -0
- package/src/pod-lease/admission.ts +150 -0
- package/src/pod-lease/dependency-state.ts +85 -0
- package/src/pod-lease/host-lease.ts +127 -0
- package/src/project/project-suggestion.ts +16 -0
- package/src/project/session-context.ts +24 -0
- package/src/release-loop/checklist.ts +17 -5
- package/src/release-loop/feature-store.ts +29 -13
- package/src/release-loop/landed-but-yellow.ts +48 -0
- package/src/release-loop/next-md-merge.ts +58 -0
- package/src/release-loop/release-note.ts +59 -4
- package/src/seat-dispatch/seat-ask.ts +130 -0
- package/src/seat-dispatch/seat-questions.ts +100 -0
- package/src/seat-loop/seat-loop.ts +709 -29
- package/src/seat-loop/seat-report.ts +37 -2
- package/src/self-dev/dev-cli.ts +4 -1
- package/src/self-dev/dev-pipeline.ts +37 -1
- package/src/self-dev/draft-sweep.ts +79 -40
- package/src/self-dev/draft-triage-rules.ts +62 -7
- package/src/self-dev/fabric-plan-core.ts +297 -0
- package/src/self-dev/orchestrate.ts +36 -4
- package/src/self-dev/release-path-guard.ts +62 -0
- package/src/self-dev/shard-aggregate.ts +230 -0
- package/src/self-dev/shard-placement.ts +60 -0
- package/src/self-dev/shard-plan.ts +48 -0
- package/src/self-dev/shard-pod-run.ts +77 -0
- package/src/self-dev/shard-run.ts +105 -0
- package/src/self-implement/budget-gate.ts +29 -12
- package/src/self-implement/gate-baseline.ts +1 -1
- package/src/self-implement/gate-candidate-ranking.ts +60 -0
- package/src/self-implement/gate-cli.ts +40 -1
- package/src/self-implement/gate-scope.ts +44 -13
- package/src/self-implement/graph-variant.ts +171 -0
- package/src/self-implement/harness-policy-gates.ts +1 -1
- package/src/self-implement/headless-elanous-driver.ts +27 -3
- package/src/self-implement/host-regate.ts +22 -1
- package/src/self-implement/importer-test-index.ts +2 -1
- package/src/self-implement/l8-merge-queue.ts +130 -0
- package/src/self-implement/orchestrator.ts +257 -9
- package/src/self-implement/route-consumer-test-index.ts +122 -0
- package/src/self-implement/seams.ts +72 -11
- package/src/self-implement/test-seams.ts +2 -0
- package/src/self-implement/uncommitted-work.ts +59 -0
- package/src/session/index.ts +36 -13
- package/src/skills/gift-install.ts +215 -0
- package/src/skills/tools/web-search.ts +4 -1
- package/src/steward/launch.ts +42 -26
- package/src/steward/steward-cards.ts +17 -9
- package/src/steward/triage.ts +46 -14
- package/src/task-cards/card-cli.ts +25 -1
- package/src/task-orchestrator/mission.ts +5 -0
- package/src/task-orchestrator/store.ts +12 -3
- package/src/task-orchestrator/surfaces/pod-command-job.ts +43 -17
- package/src/task-orchestrator/surfaces/pod-lease.ts +156 -19
- package/src/task-orchestrator/surfaces/pod-pool.ts +127 -26
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +222 -40
- package/src/task-orchestrator/surfaces/self-implement.ts +15 -2
- package/src/task-orchestrator/types.ts +2 -0
- package/src/telegram-card-followup.ts +16 -96
- package/src/telegram-commands.ts +31 -1
- package/src/telegram-project-command.ts +145 -0
- package/src/telegram-report.ts +21 -17
- package/src/telegram.ts +61 -8
- package/src/tool-runtime/self-cognition-runtimes.ts +13 -3
- package/src/user-config.ts +110 -24
- package/src/version/packed-revision.json +1 -1
- package/src/web-search/grok.ts +7 -1
- package/src/web-search/index.ts +28 -1
- package/src/web-search/provider.ts +4 -0
- package/src/web-search/x-intent.ts +5 -0
- package/src/workflow-runtime/executor.ts +277 -47
- package/src/workflow-runtime/index.ts +1 -0
- package/src/workflow-runtime/nodes/subworkflow.ts +60 -0
- package/src/workflow-runtime/plugin-kind-node.ts +1 -1
- package/src/workflow-runtime/schema.ts +79 -4
- package/src/workflow-runtime/types.ts +35 -4
- package/apps/pwa/out/_next/static/2DsiDfDCks44g1cPUMhiW/_buildManifest.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1377-1efeffbbac0963ff.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1470.98a6ab61ccb4131c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/216-5acd01cbde4acda7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2330-c2f07246c8788af1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2375-cb6debea56cce4fb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/24-e6d28380efab7faf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2977-e38442e86289f1d3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2982-2a2df543be3d2e5a.js +0 -25
- package/apps/pwa/out/_next/static/chunks/3403.4bc97b4066945a94.js +0 -5
- package/apps/pwa/out/_next/static/chunks/3615-740dd270764ffa24.js +0 -1
- package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3734-1015fe991eb9b7e7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5012-a7fe6f3c8b6fe45a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5783-ef40ebfc91d55230.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5791-bb82743fa6299562.js +0 -1
- package/apps/pwa/out/_next/static/chunks/62-ff227c3edb8b248b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6320-baac91d68843a9a6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7633-1b3709b0854c9412.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7647.2fcd30d894d5ee30.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7951-8b03dbb10b24cd06.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8142-744998d65d7bb17e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8195-6fde3d7a39859206.js +0 -10
- package/apps/pwa/out/_next/static/chunks/8619-4365e03a2862830f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8894-b5fb36bab837cd29.js +0 -1
- package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9454-84d0d5d77446bd92.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9651-3cd291a6fa1370d2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/988-5f92e7ab7ccea3dd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9989-5582582f06429840.js +0 -1
- package/apps/pwa/out/_next/static/chunks/ae787e4c-f2998de7f1ba72a4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-859cea1570b6227a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-d2253d7e58ef1890.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-5441d4a9ab35e518.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-d9b12d680922d980.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-e268d6d145827a22.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-f8116559c7424da7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-1085399fd0f32112.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-1df686a14f65895a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-54e1f966ab60a4dc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-7ed95f296f5ee8e9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-11edac668d77e5c3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-59e2b9a22cd5a6d5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-859cea1570b6227a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-2976df21ad74ab48.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-34d211a67f16d510.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-8ff652d5d950639b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-b31610deda76337b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-6cb0e1bbefe5fb3d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-a286a152cf584576.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-d9237581e7003bcb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-83274e761643fdd2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-ceee6cfb1d4e6d68.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-9c9f8bb3e4a64091.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-c17217603ea3426a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-efb6177cbf1e8a51.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-d307523703bc4df3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-462f4e4bab45ce7d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-8990e96f33af3837.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-e168fa1030af0548.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-1233e05a4068bc9c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-13a1840b1fac337a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-859cea1570b6227a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-64a29fc80b11ab55.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-68e787339a16b3dc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-4fddc90b23feb215.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-886ba8260dc79c79.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-0f26507a1722e40b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-109d4ccd42259c9d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-f5c2a4e510726774.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-b2e0039416f42522.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-26102fd7350f171e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-708948bc8a802794.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-5e5611620f78fb0b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-139ba5c367bbf60a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-ea806bb57f2b8923.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-8d2302399c2c8f87.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-202b4e81f33895ea.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-b10928c60f0b8488.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-689039227d443fab.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-e5bea36f183b25e6.js +0 -1
- package/apps/pwa/out/_next/static/css/43feca5fd387dcbd.css +0 -1
- /package/apps/pwa/out/_next/static/{2DsiDfDCks44g1cPUMhiW → -6XrAoD3eU7NyMHAE4xPS}/_ssgManifest.js +0 -0
|
@@ -138,6 +138,7 @@ interface MissionRow {
|
|
|
138
138
|
priority: MissionPriority | null;
|
|
139
139
|
task_ids_json: string; // JSON array of taskId
|
|
140
140
|
goal_slug: string | null;
|
|
141
|
+
project_id: string | null;
|
|
141
142
|
notes_json: string; // JSON array of string
|
|
142
143
|
autopilot_json: string | null; // JSON MissionAutopilot | null (U1)
|
|
143
144
|
}
|
|
@@ -309,6 +310,8 @@ export class TaskStore {
|
|
|
309
310
|
// Mission Fabric 통합 U1 (v3) — tox_missions.autopilot_json:
|
|
310
311
|
// PFC Layer2 자율 메타(apm Mission 흡수). NULL = 일반 Mission.
|
|
311
312
|
this.addColumnIfMissing('tox_missions', 'autopilot_json', 'TEXT');
|
|
313
|
+
this.addColumnIfMissing('tox_missions', 'project_id', 'TEXT');
|
|
314
|
+
this.db.exec('CREATE INDEX IF NOT EXISTS idx_tox_missions_project ON tox_missions(project_id)');
|
|
312
315
|
this.addColumnIfMissing('tox_executions', 'host_id', 'TEXT');
|
|
313
316
|
this.addColumnIfMissing('tox_executions', 'hostname', 'TEXT');
|
|
314
317
|
this.db.exec('CREATE INDEX IF NOT EXISTS idx_tox_executions_host ON tox_executions(host_id, started_at)');
|
|
@@ -587,8 +590,8 @@ export class TaskStore {
|
|
|
587
590
|
`INSERT OR REPLACE INTO tox_missions (
|
|
588
591
|
id, created_at, updated_at, closed_at, title, description, intent,
|
|
589
592
|
source_json, status, priority, task_ids_json, goal_slug, notes_json,
|
|
590
|
-
autopilot_json
|
|
591
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
593
|
+
autopilot_json, project_id
|
|
594
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
592
595
|
)
|
|
593
596
|
.run(
|
|
594
597
|
mission.id,
|
|
@@ -605,6 +608,7 @@ export class TaskStore {
|
|
|
605
608
|
mission.goalSlug ?? null,
|
|
606
609
|
JSON.stringify([...mission.notes]),
|
|
607
610
|
mission.autopilot ? JSON.stringify(mission.autopilot) : null,
|
|
611
|
+
mission.projectId ?? null,
|
|
608
612
|
));
|
|
609
613
|
}
|
|
610
614
|
|
|
@@ -616,7 +620,7 @@ export class TaskStore {
|
|
|
616
620
|
return this.rowToMission(row);
|
|
617
621
|
}
|
|
618
622
|
|
|
619
|
-
listMissions(opts: { status?: MissionStatus; goalSlug?: string } = {}): Mission[] {
|
|
623
|
+
listMissions(opts: { status?: MissionStatus; goalSlug?: string; projectId?: string } = {}): Mission[] {
|
|
620
624
|
const where: string[] = [];
|
|
621
625
|
const params: Array<string | number | null> = [];
|
|
622
626
|
if (opts.status !== undefined) {
|
|
@@ -627,6 +631,10 @@ export class TaskStore {
|
|
|
627
631
|
where.push('goal_slug = ?');
|
|
628
632
|
params.push(opts.goalSlug);
|
|
629
633
|
}
|
|
634
|
+
if (opts.projectId !== undefined) {
|
|
635
|
+
where.push('project_id = ?');
|
|
636
|
+
params.push(opts.projectId);
|
|
637
|
+
}
|
|
630
638
|
const sql =
|
|
631
639
|
'SELECT * FROM tox_missions' +
|
|
632
640
|
(where.length > 0 ? ' WHERE ' + where.join(' AND ') : '') +
|
|
@@ -729,6 +737,7 @@ export class TaskStore {
|
|
|
729
737
|
priority: row.priority ?? undefined,
|
|
730
738
|
taskIds: Object.freeze(JSON.parse(row.task_ids_json) as string[]),
|
|
731
739
|
goalSlug: row.goal_slug ?? undefined,
|
|
740
|
+
...(row.project_id != null ? { projectId: row.project_id } : {}),
|
|
732
741
|
notes: Object.freeze(JSON.parse(row.notes_json) as string[]),
|
|
733
742
|
autopilot: row.autopilot_json
|
|
734
743
|
? (JSON.parse(row.autopilot_json) as Mission['autopilot'])
|
|
@@ -7,14 +7,14 @@
|
|
|
7
7
|
|
|
8
8
|
import { randomBytes } from 'node:crypto';
|
|
9
9
|
import { spawnSync } from 'node:child_process';
|
|
10
|
-
import { isAbsolute } from 'node:path';
|
|
10
|
+
import { isAbsolute, resolve } from 'node:path';
|
|
11
11
|
import { getUserConfig } from '../../user-config.js';
|
|
12
12
|
import { debug } from '../../debug/log.js';
|
|
13
13
|
import { effectiveInstanceRoot } from '../../instance/resolve.js';
|
|
14
14
|
import { collectPodArtifacts } from './pod-artifact-return.js';
|
|
15
15
|
import { podBunCacheVolume } from './pod-bun-cache.js';
|
|
16
16
|
import { type PodPoolMember, PodPoolScheduler, resolvePodPoolSpec, parsePodPool, checkPodPool, syncPoolImages, type PoolKubectl } from './pod-pool.js';
|
|
17
|
-
import { readSkillEnvFiles } from './pod-skills.js';
|
|
17
|
+
import { podSkillsDigest, readSkillEnvFiles, resolvePodSkills } from './pod-skills.js';
|
|
18
18
|
import { podSourceScript, type PodSource } from './pod-source-receive.js';
|
|
19
19
|
import {
|
|
20
20
|
POD_CHILD_REQUESTS,
|
|
@@ -30,6 +30,24 @@ import {
|
|
|
30
30
|
/** CLI `--deadline` 생략 시 — 기존 Pod Job 수명 상한과 같다. */
|
|
31
31
|
export const POD_COMMAND_DEADLINE_SECONDS = POD_JOB_DEADLINE_SECONDS;
|
|
32
32
|
|
|
33
|
+
export const POD_NETWORK_SKILLS = ['omni-crawl', 'omni-digest'] as const;
|
|
34
|
+
export const POD_LITE_IMAGE = 'elanous-harness-lite:local';
|
|
35
|
+
|
|
36
|
+
export const POD_LITE_MEMORY_LIMIT = '2Gi';
|
|
37
|
+
|
|
38
|
+
/** POD7 must-fix: the skill list alone can't tell a network-only job from one that needs ffmpeg/browser — so lite is
|
|
39
|
+
* only ever chosen when the caller says so (`lite`), and then only for network skills without a clone. Anything we
|
|
40
|
+
* can't judge keeps the full image. A lite request that doesn't fit is refused with the reason, not silently widened. */
|
|
41
|
+
export function podCommandImageFor(skills: readonly string[], clone: boolean, lite = false): string {
|
|
42
|
+
if (!lite) return 'elanous-harness:local';
|
|
43
|
+
if (clone) throw new Error('pod command: --lite 는 clone 없는 명령만 받는다(lite 이미지는 git·빌드 도구가 적다)');
|
|
44
|
+
const outside = skills.filter((skill) => !(POD_NETWORK_SKILLS as readonly string[]).includes(skill));
|
|
45
|
+
if (skills.length === 0 || outside.length > 0) {
|
|
46
|
+
throw new Error(`pod command: --lite 는 네트워크 스킬(${POD_NETWORK_SKILLS.join('·')})만 받는다${outside.length ? ` — 벗어난 스킬: ${outside.join(', ')}` : ' — --skill 이 없다'}`);
|
|
47
|
+
}
|
|
48
|
+
return POD_LITE_IMAGE;
|
|
49
|
+
}
|
|
50
|
+
|
|
33
51
|
const COMMAND_LOG_CHARS = 80;
|
|
34
52
|
|
|
35
53
|
const GATE = `ok=0
|
|
@@ -212,8 +230,10 @@ export interface RunPodCommandOptions {
|
|
|
212
230
|
clone?: boolean;
|
|
213
231
|
hostMirror?: string;
|
|
214
232
|
bunCache?: string;
|
|
215
|
-
/** 컨테이너 메모리 한도(기본 16Gi). */
|
|
233
|
+
/** 컨테이너 메모리 한도(기본 16Gi · lite 면 2Gi). */
|
|
216
234
|
memoryLimit?: string;
|
|
235
|
+
/** POD7: 호출부가 «네트워크 스킬만 쓰는 명령»이라고 말할 때만 lite 이미지 ⊕ 2Gi. */
|
|
236
|
+
lite?: boolean;
|
|
217
237
|
source?: PodSource;
|
|
218
238
|
ghToken?: () => string;
|
|
219
239
|
deadlineSeconds?: number;
|
|
@@ -266,7 +286,8 @@ export async function runPodCommand(options: RunPodCommandOptions): Promise<PodC
|
|
|
266
286
|
const baseKubectl = options.kubectl ?? defaultKubectl;
|
|
267
287
|
const sleep = options.sleep ?? ((ms: number) => new Promise<void>((r) => setTimeout(r, ms)));
|
|
268
288
|
const namespace = options.namespace ?? 'elanous-test';
|
|
269
|
-
const image = options.image ??
|
|
289
|
+
const image = options.image ?? podCommandImageFor(skills, options.clone === true, options.lite === true);
|
|
290
|
+
const memoryLimit = options.memoryLimit ?? (image === POD_LITE_IMAGE ? POD_LITE_MEMORY_LIMIT : undefined);
|
|
270
291
|
const repoUrl = options.repoUrl ?? 'https://github.com/ElanvitalAI/elanous';
|
|
271
292
|
const deadlineSeconds = options.deadlineSeconds ?? POD_COMMAND_DEADLINE_SECONDS;
|
|
272
293
|
// POD1 (10-01): two launches in the same moment got the same `si-cmd-<time>` and the failing one's cleanup
|
|
@@ -283,13 +304,15 @@ export async function runPodCommand(options: RunPodCommandOptions): Promise<PodC
|
|
|
283
304
|
|
|
284
305
|
let member: PodPoolMember | null = null;
|
|
285
306
|
let pool = options.poolScheduler;
|
|
286
|
-
if (!pool
|
|
287
|
-
const spec = resolvePodPoolSpec(options.pool, env);
|
|
288
|
-
if (!spec) throw new Error('pod command: 풀 스펙이 비었다');
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
307
|
+
if (!pool) {
|
|
308
|
+
const spec = resolvePodPoolSpec(options.pool, env, () => options.kubectl ? undefined : getUserConfig().pod?.pool);
|
|
309
|
+
if (options.pool && !spec) throw new Error('pod command: 풀 스펙이 비었다');
|
|
310
|
+
if (spec) {
|
|
311
|
+
const members = parsePodPool(spec);
|
|
312
|
+
const checked = (options.checkPool ?? checkPodPool)(members, baseKubectl as PoolKubectl);
|
|
313
|
+
if (!checked.ok) throw new Error('pod command: 풀의 노드가 하나도 준비되지 않았다');
|
|
314
|
+
pool = new PodPoolScheduler(checked.ready);
|
|
315
|
+
}
|
|
293
316
|
}
|
|
294
317
|
if (pool) {
|
|
295
318
|
for (;;) {
|
|
@@ -328,13 +351,16 @@ export async function runPodCommand(options: RunPodCommandOptions): Promise<PodC
|
|
|
328
351
|
localImageCommit: () => podImageFreshness({ image }).imageCommit,
|
|
329
352
|
});
|
|
330
353
|
if (pool && member) {
|
|
331
|
-
const
|
|
332
|
-
|
|
354
|
+
const localSkillsDigest = options.kubectl ? null : image === POD_LITE_IMAGE
|
|
355
|
+
? podSkillsDigest(POD_NETWORK_SKILLS, resolve(import.meta.dir, '../../../skills')).digest
|
|
356
|
+
: podSkillsDigest(resolvePodSkills(env).skills).digest;
|
|
333
357
|
const syncs = await (options.syncImages ?? syncPoolImages)([member], image, imageCommit, { localSkillsDigest });
|
|
334
358
|
const synced = syncs.get(member.context);
|
|
335
359
|
if (synced && !synced.ok) throw new Error(`pod command: 이미지 판을 못 맞췄다: ${synced.detail}`);
|
|
336
360
|
if (synced?.imageRef) member = { ...member, imageRef: synced.imageRef };
|
|
337
361
|
}
|
|
362
|
+
const imageRef = member?.imageRef;
|
|
363
|
+
if (!imageRef) log('pod.command-job', 'image-fallback-local', { reason: !member ? 'no-pool-member' : !imageCommit ? 'image-commit-unavailable' : 'registry-tag-unavailable' });
|
|
338
364
|
const ghToken = options.clone ? (options.ghToken ?? defaultGhToken)() : undefined;
|
|
339
365
|
const secret = commandJobSecret({ name, namespace, launch, skills, skillEnvText, ...(grokAuth !== undefined ? { grokAuth } : {}), ...(ghToken !== undefined ? { ghToken } : {}) });
|
|
340
366
|
if (secret) {
|
|
@@ -342,10 +368,10 @@ export async function runPodCommand(options: RunPodCommandOptions): Promise<PodC
|
|
|
342
368
|
if (applied.status !== 0) throw new Error(applied.stderr.trim() || 'pod command: Secret 적용 실패');
|
|
343
369
|
}
|
|
344
370
|
const manifest = podCommandJobManifest({
|
|
345
|
-
name, namespace, launch, image:
|
|
346
|
-
...(
|
|
371
|
+
name, namespace, launch, image: imageRef ?? image,
|
|
372
|
+
...(imageRef ? { imagePullPolicy: 'IfNotPresent' as const } : {}),
|
|
347
373
|
repoUrl, command, skills, ...(llm ? { llm } : {}), ...(options.clone ? { clone: true } : {}),
|
|
348
|
-
...(options.source ? { source: options.source } : {}), ...(hostMirror ? { hostMirror } : {}), ...(bunCache ? { bunCache } : {}), ...(
|
|
374
|
+
...(options.source ? { source: options.source } : {}), ...(hostMirror ? { hostMirror } : {}), ...(bunCache ? { bunCache } : {}), ...(memoryLimit ? { memoryLimit } : {}), deadlineSeconds,
|
|
349
375
|
...(options.runId ? { runId: options.runId } : {}),
|
|
350
376
|
});
|
|
351
377
|
kubectl(['-n', namespace, 'delete', 'job', '-l', own, '--ignore-not-found']);
|
|
@@ -376,7 +402,7 @@ export async function runPodCommand(options: RunPodCommandOptions): Promise<PodC
|
|
|
376
402
|
llm: llm ?? null,
|
|
377
403
|
command: command.join(' ').slice(0, COMMAND_LOG_CHARS),
|
|
378
404
|
});
|
|
379
|
-
return { exitCode, artifactsDir, job: name, image:
|
|
405
|
+
return { exitCode, artifactsDir, job: name, image: imageRef ?? image };
|
|
380
406
|
} catch (err) {
|
|
381
407
|
cleanupSecret();
|
|
382
408
|
throw err;
|
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
import type { PodPoolMember, PoolKubectl } from './pod-pool.js';
|
|
2
|
+
import { debug } from '../../debug/log.js';
|
|
2
3
|
import { spawnSync } from 'node:child_process';
|
|
4
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
5
|
+
import { closeSync, mkdirSync, openSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
import { dlopen } from 'bun:ffi';
|
|
8
|
+
import { hostLeaseBaseDir } from '../../pod-lease/host-lease.js';
|
|
9
|
+
|
|
10
|
+
/** Persisted on a Job admitted through the host lease; the file lease is released after apply. */
|
|
11
|
+
export const POD_HOST_LEASE_ANNOTATION = 'elanous.dev/host-lease-admitted';
|
|
3
12
|
|
|
4
13
|
// `get pods --all-namespaces -o json` on a busy cluster is several MB — the default spawnSync
|
|
5
14
|
// buffer fails with ENOBUFS (10-03 node-b measured). Same proxy stripping as the Pod launch kubectl.
|
|
6
15
|
const LEASE_KUBECTL_MAX_BUFFER = 256 * 1024 * 1024;
|
|
7
|
-
export function leaseKubectl(args: readonly string[]): { status: number | null; stdout: string; stderr: string } {
|
|
16
|
+
export function leaseKubectl(args: readonly string[], input?: string): { status: number | null; stdout: string; stderr: string } {
|
|
8
17
|
const env = { ...process.env };
|
|
9
18
|
for (const k of ['HTTPS_PROXY', 'https_proxy', 'HTTP_PROXY', 'http_proxy', 'ALL_PROXY', 'all_proxy']) delete env[k];
|
|
10
|
-
const r = spawnSync('kubectl', [...args], { encoding: 'utf8', env, timeout: 120_000, maxBuffer: LEASE_KUBECTL_MAX_BUFFER });
|
|
19
|
+
const r = spawnSync('kubectl', [...args], { encoding: 'utf8', input, env, timeout: 120_000, maxBuffer: LEASE_KUBECTL_MAX_BUFFER });
|
|
11
20
|
return { status: r.status, stdout: r.stdout ?? '', stderr: (r.stderr ?? '') + (r.error ? String(r.error) : '') };
|
|
12
21
|
}
|
|
13
22
|
|
|
@@ -16,6 +25,8 @@ export interface PodLeaseMember {
|
|
|
16
25
|
capacity: number;
|
|
17
26
|
running: number | null;
|
|
18
27
|
pending: number | null;
|
|
28
|
+
/** Running harness Pods whose Jobs predate host lease admission. */
|
|
29
|
+
unleasedRunning: number | null;
|
|
19
30
|
memoryLimitBytes: number | null;
|
|
20
31
|
/** All-node allocatable memory, before existing reservations. */
|
|
21
32
|
allocatableMemoryBytes: number | null;
|
|
@@ -35,10 +46,14 @@ export interface PoolLeaseRecommendation {
|
|
|
35
46
|
reason: string | null;
|
|
36
47
|
capacitySlots: number | null;
|
|
37
48
|
memorySlots: number | null;
|
|
49
|
+
/** POD7 — the same memory headroom counted in lite goals (2Gi) · observation only: `recommended` stays on the standard size. */
|
|
50
|
+
liteMemorySlots?: number | null;
|
|
38
51
|
placeableSlots: number | null;
|
|
39
52
|
accountSlots: number | null;
|
|
40
53
|
running: number | null;
|
|
41
54
|
pending: number | null;
|
|
55
|
+
/** Absent only for injected legacy status fixtures; real measurements always report a number or null. */
|
|
56
|
+
unleasedRunning?: number | null;
|
|
42
57
|
}
|
|
43
58
|
|
|
44
59
|
/** Kubernetes quantity to bytes (memory) or millicores (CPU). */
|
|
@@ -110,12 +125,114 @@ function runningLimit(spec: Record<string, unknown>): number | null {
|
|
|
110
125
|
return total;
|
|
111
126
|
}
|
|
112
127
|
|
|
113
|
-
/**
|
|
114
|
-
export
|
|
128
|
+
/** A short-lived Pod proves cluster DNS from inside the same namespace as harness Jobs. Image pull failures are not DNS failures. */
|
|
129
|
+
export type PoolDnsProbe = 'ready' | 'dns' | 'image' | 'unknown';
|
|
130
|
+
const DNS_CACHE_MS = 10 * 60_000;
|
|
131
|
+
const DNS_LOCK_WAIT_MS = 6 * 60_000;
|
|
132
|
+
// flock is released by the kernel on process exit, including a crash before any cache write.
|
|
133
|
+
// The lock file is never unlinked: unlinking it would let waiters lock different inodes.
|
|
134
|
+
const dnsFlock = process.platform === 'linux' || process.platform === 'darwin'
|
|
135
|
+
? dlopen(process.platform === 'linux' ? 'libc.so.6' : 'libSystem.B.dylib', {
|
|
136
|
+
flock: { args: ['int', 'int'], returns: 'int' },
|
|
137
|
+
}).symbols.flock : null;
|
|
138
|
+
|
|
139
|
+
/** The cache and lock are shared by CLI processes, keyed by context rather than pool composition. */
|
|
140
|
+
export function probePoolDns(context: string, kubectl: PoolKubectl = leaseKubectl, options: { dir?: string; now?: () => number } = {}): PoolDnsProbe {
|
|
141
|
+
const dir = options.dir ?? hostLeaseBaseDir();
|
|
142
|
+
const now = options.now ?? Date.now;
|
|
143
|
+
const key = createHash('sha256').update(context).digest('hex');
|
|
144
|
+
const cache = join(dir, `dns-${key}.cache`);
|
|
145
|
+
const outcome = join(dir, `dns-${key}.outcome`);
|
|
146
|
+
const lock = join(dir, `dns-${key}.flock`);
|
|
147
|
+
const readRecord = (path: string): Record<string, unknown> | null => {
|
|
148
|
+
try { return object(JSON.parse(readFileSync(path, 'utf8'))); }
|
|
149
|
+
catch { return null; }
|
|
150
|
+
};
|
|
151
|
+
const cached = (): boolean => {
|
|
152
|
+
const record = readRecord(cache);
|
|
153
|
+
return record?.result === 'ready' && typeof record.at === 'number' &&
|
|
154
|
+
now() >= record.at && now() - record.at < DNS_CACHE_MS;
|
|
155
|
+
};
|
|
156
|
+
if (cached()) return 'ready';
|
|
157
|
+
const previous = readRecord(outcome);
|
|
158
|
+
try { mkdirSync(dir, { recursive: true, mode: 0o700 }); } catch { return 'unknown'; }
|
|
159
|
+
if (!dnsFlock) return 'unknown';
|
|
160
|
+
let fd: number;
|
|
161
|
+
try { fd = openSync(lock, 'a', 0o600); } catch { return 'unknown'; }
|
|
162
|
+
const deadline = Date.now() + DNS_LOCK_WAIT_MS;
|
|
163
|
+
let held = false;
|
|
164
|
+
try {
|
|
165
|
+
const sharedFailure = (): PoolDnsProbe | null => {
|
|
166
|
+
const record = readRecord(outcome);
|
|
167
|
+
// Only a caller that started before this generation was written can reuse it.
|
|
168
|
+
if (record?.generation !== undefined && record.generation !== previous?.generation &&
|
|
169
|
+
['dns', 'image', 'unknown'].includes(String(record.result))) return record.result as PoolDnsProbe;
|
|
170
|
+
return null;
|
|
171
|
+
};
|
|
172
|
+
while (!held) {
|
|
173
|
+
if (cached()) return 'ready';
|
|
174
|
+
const shared = sharedFailure();
|
|
175
|
+
if (shared) return shared;
|
|
176
|
+
if (dnsFlock(fd, 2 | 4) === 0) { held = true; break; } // LOCK_EX | LOCK_NB
|
|
177
|
+
if (Date.now() >= deadline) return 'unknown';
|
|
178
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 100);
|
|
179
|
+
}
|
|
180
|
+
if (cached()) return 'ready';
|
|
181
|
+
const shared = sharedFailure();
|
|
182
|
+
if (shared) return shared;
|
|
183
|
+
const result = runPoolDnsProbe(context, kubectl);
|
|
184
|
+
const destination = result === 'ready' ? cache : outcome;
|
|
185
|
+
const temp = `${destination}.${randomUUID()}.tmp`;
|
|
186
|
+
try {
|
|
187
|
+
writeFileSync(temp, JSON.stringify({ result, at: now(), generation: randomUUID() }), { mode: 0o600 });
|
|
188
|
+
renameSync(temp, destination);
|
|
189
|
+
} catch { try { rmSync(temp, { force: true }); } catch { /* best-effort cleanup */ } }
|
|
190
|
+
return result;
|
|
191
|
+
} finally {
|
|
192
|
+
if (held) dnsFlock(fd, 8); // LOCK_UN
|
|
193
|
+
closeSync(fd);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function runPoolDnsProbe(context: string, kubectl: PoolKubectl): PoolDnsProbe {
|
|
198
|
+
const name = `elanous-dns-${randomUUID().slice(0, 12)}`;
|
|
199
|
+
const base = ['--context', context, '--request-timeout=10s'];
|
|
200
|
+
try {
|
|
201
|
+
const core = kubectl([...base, '-n', 'kube-system', 'get', 'deployment', 'coredns', '-o', 'json']);
|
|
202
|
+
if (core.status !== 0) return 'unknown';
|
|
203
|
+
const deployment = object(JSON.parse(core.stdout));
|
|
204
|
+
const spec = object(deployment?.spec);
|
|
205
|
+
const status = object(deployment?.status);
|
|
206
|
+
if (typeof spec?.replicas !== 'number' || spec.replicas < 1 || status?.readyReplicas !== spec.replicas) return 'dns';
|
|
207
|
+
const manifest = JSON.stringify({ apiVersion: 'v1', kind: 'Pod', metadata: { name, namespace: 'elanous-test', labels: { 'elanous.probe': 'dns' } }, spec: {
|
|
208
|
+
restartPolicy: 'Never', activeDeadlineSeconds: 40, automountServiceAccountToken: false,
|
|
209
|
+
containers: [{ name: 'dns', image: 'busybox:1.36', command: ['nslookup', 'kubernetes.default.svc.cluster.local'], resources: { requests: { cpu: '10m', memory: '16Mi' }, limits: { cpu: '100m', memory: '64Mi' } } }],
|
|
210
|
+
} });
|
|
211
|
+
if (kubectl([...base, 'create', '-f', '-'], manifest).status !== 0) return 'unknown';
|
|
212
|
+
const waited = kubectl([...base, '-n', 'elanous-test', 'wait', '--for=jsonpath={.status.phase}=Succeeded', `pod/${name}`, '--timeout=30s']);
|
|
213
|
+
const pod = kubectl([...base, '-n', 'elanous-test', 'get', `pod/${name}`, '-o', 'json']);
|
|
214
|
+
if (pod.status !== 0) return 'unknown';
|
|
215
|
+
const state = object(JSON.parse(pod.stdout));
|
|
216
|
+
const phase = object(state?.status)?.phase;
|
|
217
|
+
const statuses = object(state?.status)?.containerStatuses;
|
|
218
|
+
if (Array.isArray(statuses) && statuses.some((c) =>
|
|
219
|
+
['ErrImagePull', 'ImagePullBackOff', 'InvalidImageName'].includes(String(object(object(object(c)?.state)?.waiting)?.reason)))) return 'image';
|
|
220
|
+
if (phase !== 'Succeeded' && phase !== 'Failed') return 'unknown';
|
|
221
|
+
if (waited.status !== 0 && phase !== 'Failed') return 'unknown';
|
|
222
|
+
const log = kubectl([...base, '-n', 'elanous-test', 'logs', `pod/${name}`]);
|
|
223
|
+
if (log.status !== 0) return 'unknown';
|
|
224
|
+
if (phase === 'Failed') return /can't resolve|server can't find|connection timed out|no servers could be reached|NXDOMAIN|SERVFAIL/iu.test(log.stdout) ? 'dns' : 'unknown';
|
|
225
|
+
return /\bName:\s*kubernetes\.default\.svc\.cluster\.local\b/u.test(log.stdout) && /\bAddress(?:es)?:\s*\S+/u.test(log.stdout) ? 'ready' : 'dns';
|
|
226
|
+
} catch { return 'unknown'; }
|
|
227
|
+
finally { try { kubectl([...base, '-n', 'elanous-test', 'delete', `pod/${name}`, '--ignore-not-found=true', '--wait=false']); } catch { /* best-effort cleanup */ } }
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/** Each cluster reads resource reservations; DNS requires a bounded disposable Pod. */
|
|
231
|
+
export function measurePoolLease(members: readonly PodPoolMember[], deps: { kubectl?: PoolKubectl; dns?: (context: string) => PoolDnsProbe } = {}): PoolLeaseMeasure {
|
|
115
232
|
const kubectl = deps.kubectl ?? leaseKubectl;
|
|
116
233
|
return { members: members.map((member) => {
|
|
117
234
|
const base = ['--context', member.context, '--request-timeout=10s'];
|
|
118
|
-
const result: PodLeaseMember = { context: member.context, capacity: member.capacity, running: null, pending: null, memoryLimitBytes: null, allocatableMemoryBytes: null, allocatableCpuMillicores: null, availableMemoryByNodeBytes: null, reason: null };
|
|
235
|
+
const result: PodLeaseMember = { context: member.context, capacity: member.capacity, running: null, pending: null, unleasedRunning: null, memoryLimitBytes: null, allocatableMemoryBytes: null, allocatableCpuMillicores: null, availableMemoryByNodeBytes: null, reason: null };
|
|
119
236
|
try {
|
|
120
237
|
const nodes = kubectl([...base, 'get', 'nodes', '-o', 'json']);
|
|
121
238
|
const jobs = kubectl([...base, '-n', 'elanous-test', 'get', 'jobs', '-l', 'elanous.substrate=pod', '-o', 'json']);
|
|
@@ -165,6 +282,7 @@ export function measurePoolLease(members: readonly PodPoolMember[], deps: { kube
|
|
|
165
282
|
if (!podItems) reasons.push(`cluster pods: ${pods.status === 0 ? 'invalid response' : pods.stderr.trim().split('\n').pop() || `rc=${pods.status}`}`);
|
|
166
283
|
if (podItems && jobItems) {
|
|
167
284
|
const jobNames = new Set<string>();
|
|
285
|
+
const leasedJobs = new Set<string>();
|
|
168
286
|
let jobsValid = true;
|
|
169
287
|
for (const job of jobItems) {
|
|
170
288
|
const meta = object(job.metadata);
|
|
@@ -172,9 +290,10 @@ export function measurePoolLease(members: readonly PodPoolMember[], deps: { kube
|
|
|
172
290
|
if (object(meta.labels)?.['elanous.substrate'] !== 'pod') continue;
|
|
173
291
|
if (typeof meta.name !== 'string' || !meta.name) { jobsValid = false; continue; }
|
|
174
292
|
jobNames.add(meta.name);
|
|
293
|
+
if (object(meta.annotations)?.[POD_HOST_LEASE_ANNOTATION] === 'true') leasedJobs.add(meta.name);
|
|
175
294
|
}
|
|
176
295
|
if (!jobsValid) reasons.push('cluster jobs: missing name/labels');
|
|
177
|
-
let running = 0, pending = 0, limits = 0, limitsValid = true, reservationsValid = nodeItems !== null && nodeNames.size === nodeItems.length;
|
|
296
|
+
let running = 0, pending = 0, unleasedRunning = 0, limits = 0, limitsValid = true, reservationsValid = nodeItems !== null && nodeNames.size === nodeItems.length;
|
|
178
297
|
let unknownPhase = false;
|
|
179
298
|
const seenPods = new Set<string>();
|
|
180
299
|
for (const pod of podItems) {
|
|
@@ -190,7 +309,7 @@ export function measurePoolLease(members: readonly PodPoolMember[], deps: { kube
|
|
|
190
309
|
if (seenPods.has(identity)) { reservationsValid = false; continue; }
|
|
191
310
|
seenPods.add(identity);
|
|
192
311
|
} else reservationsValid = false;
|
|
193
|
-
const harness = metadata?.namespace === 'elanous-test' &&
|
|
312
|
+
const harness = metadata?.namespace === 'elanous-test' && labels?.['elanous.probe'] !== 'dns' &&
|
|
194
313
|
(labels?.['elanous.substrate'] === 'pod' || jobNames.has(String(labels?.['elanous.job'] ?? '')));
|
|
195
314
|
const spec = object(pod.spec);
|
|
196
315
|
let limit: number | null = null;
|
|
@@ -198,6 +317,8 @@ export function measurePoolLease(members: readonly PodPoolMember[], deps: { kube
|
|
|
198
317
|
if (phase === 'Pending') pending++;
|
|
199
318
|
else {
|
|
200
319
|
running++;
|
|
320
|
+
const jobName = labels?.['elanous.job'] ?? labels?.['job-name'];
|
|
321
|
+
if (typeof jobName !== 'string' || !leasedJobs.has(jobName)) unleasedRunning++;
|
|
201
322
|
limit = spec ? runningLimit(spec) : null;
|
|
202
323
|
if (limit === null) limitsValid = false;
|
|
203
324
|
else limits += limit;
|
|
@@ -212,15 +333,21 @@ export function measurePoolLease(members: readonly PodPoolMember[], deps: { kube
|
|
|
212
333
|
if (nodeFree.has(nodeName)) nodeFree.set(nodeName, nodeFree.get(nodeName)! - Math.max(requested, limit ?? 0));
|
|
213
334
|
}
|
|
214
335
|
if (unknownPhase) reasons.push('cluster pods: missing/Unknown phase');
|
|
215
|
-
if (jobsValid && !unknownPhase) { result.running = running; result.pending = pending; }
|
|
336
|
+
if (jobsValid && !unknownPhase) { result.running = running; result.pending = pending; result.unleasedRunning = unleasedRunning; }
|
|
216
337
|
if (limitsValid && jobsValid && !unknownPhase) result.memoryLimitBytes = limits;
|
|
217
338
|
else if (!limitsValid) reasons.push('cluster pods: Running Pod memory limit missing/invalid');
|
|
218
339
|
if (reservationsValid && limitsValid && jobsValid && !unknownPhase) result.availableMemoryByNodeBytes = [...nodeFree.values()];
|
|
219
340
|
else if (!reservationsValid && !unknownPhase) reasons.push('cluster reservations: node assignment or memory request/limit missing/invalid');
|
|
220
341
|
}
|
|
221
342
|
result.reason = reasons.length ? reasons.join('; ').slice(0, 240) : null;
|
|
343
|
+
if (nodeItems?.length && jobItems && podItems) {
|
|
344
|
+
let dnsState: PoolDnsProbe = 'unknown';
|
|
345
|
+
try { dnsState = (deps.dns ?? ((context) => probePoolDns(context, kubectl)))(member.context); } catch { /* probe could not be measured */ }
|
|
346
|
+
if (dnsState === 'dns') { result.capacity = 0; result.availableMemoryByNodeBytes = []; result.reason = 'dns'; }
|
|
347
|
+
else if (dnsState !== 'ready') { result.availableMemoryByNodeBytes = null; result.reason = `측정 불가: dns probe ${dnsState}`; }
|
|
348
|
+
}
|
|
222
349
|
} catch (error) {
|
|
223
|
-
result.running = null; result.pending = null; result.memoryLimitBytes = null;
|
|
350
|
+
result.running = null; result.pending = null; result.unleasedRunning = null; result.memoryLimitBytes = null;
|
|
224
351
|
result.allocatableMemoryBytes = null; result.allocatableCpuMillicores = null; result.availableMemoryByNodeBytes = null;
|
|
225
352
|
result.reason = `cluster: ${error instanceof Error ? error.message : String(error)}`.split('\n')[0]!.slice(0, 240);
|
|
226
353
|
}
|
|
@@ -229,32 +356,42 @@ export function measurePoolLease(members: readonly PodPoolMember[], deps: { kube
|
|
|
229
356
|
}
|
|
230
357
|
|
|
231
358
|
/** Per-node memory and per-member slots cannot be exchanged across nodes/clusters. */
|
|
232
|
-
export function recommendConcurrency(measure: PoolLeaseMeasure, options: { capacity: number; perGoalMemory?: string; accounts: number; perAccount: number }): PoolLeaseRecommendation {
|
|
233
|
-
const empty: PoolLeaseRecommendation = { recommended: null, limitedBy: null, reason: null, capacitySlots: null, memorySlots: null, placeableSlots: null, accountSlots: null, running: null, pending: null };
|
|
359
|
+
export function recommendConcurrency(measure: PoolLeaseMeasure, options: { capacity: number; perGoalMemory?: string; liteGoalMemory?: string; accounts: number; perAccount: number }): PoolLeaseRecommendation {
|
|
360
|
+
const empty: PoolLeaseRecommendation = { recommended: null, limitedBy: null, reason: null, capacitySlots: null, memorySlots: null, liteMemorySlots: null, placeableSlots: null, accountSlots: null, running: null, pending: null, unleasedRunning: null };
|
|
234
361
|
const goalBytes = quantity(options.perGoalMemory ?? '16Gi', 'memory');
|
|
235
362
|
if (goalBytes === null || goalBytes <= 0) return { ...empty, reason: '측정 불가: perGoalMemory' };
|
|
236
|
-
if (!measure.members.length || measure.members.some((m) => m.running === null || m.pending === null || m.allocatableMemoryBytes === null || m.memoryLimitBytes === null || m.allocatableCpuMillicores === null || m.availableMemoryByNodeBytes === null)) {
|
|
363
|
+
if (!measure.members.length || measure.members.some((m) => m.running === null || m.pending === null || m.unleasedRunning === null || m.allocatableMemoryBytes === null || m.memoryLimitBytes === null || m.allocatableCpuMillicores === null || m.availableMemoryByNodeBytes === null)) {
|
|
237
364
|
return { ...empty, reason: `측정 불가: cluster${measure.members.map((m) => m.reason ? ` ${m.context}: ${m.reason}` : '').join('')}` };
|
|
238
365
|
}
|
|
239
366
|
if (!Number.isSafeInteger(options.capacity) || options.capacity < 0) return { ...empty, reason: '측정 불가: capacity' };
|
|
240
367
|
const running = measure.members.reduce((sum, m) => sum + m.running!, 0);
|
|
241
368
|
const pending = measure.members.reduce((sum, m) => sum + m.pending!, 0);
|
|
242
|
-
const
|
|
243
|
-
|
|
369
|
+
const unleasedRunning = measure.members.reduce((sum, m) => sum + m.unleasedRunning!, 0);
|
|
370
|
+
// Pool-wide free = sum of per-member free slots: a member over its cap counts as full, never negative,
|
|
371
|
+
// so it cannot zero another member's free slot (and its unleased Pods only consume its own free slots).
|
|
372
|
+
const memberFree = measure.members.map((m) => Math.max(0, m.capacity - m.running! - m.pending!));
|
|
373
|
+
const eligibleUnleasedRunning = measure.members.reduce((sum, m, i) => sum + (m.reason === 'dns' ? 0 : Math.min(m.unleasedRunning!, memberFree[i]!)), 0);
|
|
374
|
+
const eligibleOccupied = measure.members.reduce((sum, m) => sum + (m.reason === 'dns' ? 0 : Math.min(m.capacity, m.running! + m.pending!)), 0);
|
|
375
|
+
const capacitySlots = Math.max(0, Math.min(options.capacity - eligibleOccupied,
|
|
376
|
+
memberFree.reduce((sum, free) => sum + free, 0)));
|
|
244
377
|
const memberMemorySlots = measure.members.map((m) => m.availableMemoryByNodeBytes!.reduce(
|
|
245
378
|
(sum, bytes) => sum + Math.max(0, Math.floor(bytes / goalBytes)), 0));
|
|
246
379
|
// Preserve the requested allocatable-minus-harness-limit metric. The node-local
|
|
247
380
|
// placement bound additionally accounts for every namespace's reservations.
|
|
248
|
-
const memorySlots = measure.members.reduce((sum, m) => sum + Math.max(0,
|
|
249
|
-
Math.floor((m.allocatableMemoryBytes! - m.memoryLimitBytes!) / goalBytes)), 0);
|
|
381
|
+
const memorySlots = measure.members.reduce((sum, m) => sum + (m.reason === 'dns' ? 0 : Math.max(0,
|
|
382
|
+
Math.floor((m.allocatableMemoryBytes! - m.memoryLimitBytes!) / goalBytes))), 0);
|
|
250
383
|
const placeableSlots = measure.members.reduce((sum, m, i) => sum + Math.min(
|
|
251
384
|
Math.max(0, m.capacity - m.running! - m.pending!), memberMemorySlots[i]!,
|
|
252
385
|
), 0);
|
|
253
386
|
// Account count is not a concurrency limit (same IP · own accounts, 10-03 decision) — observed only, never a bound.
|
|
254
387
|
const accountSlots = [options.accounts, options.perAccount].every((n) => Number.isSafeInteger(n) && n >= 0)
|
|
255
388
|
? Math.max(0, options.accounts * options.perAccount - running) : null;
|
|
389
|
+
const liteBytes = quantity(options.liteGoalMemory ?? '2Gi', 'memory');
|
|
390
|
+
const liteMemorySlots = liteBytes === null || liteBytes <= 0 ? null : measure.members.reduce((sum, m) => sum + (m.reason === 'dns' ? 0 : Math.max(0,
|
|
391
|
+
Math.floor((m.allocatableMemoryBytes! - m.memoryLimitBytes!) / liteBytes))), 0);
|
|
256
392
|
const memoryBound = Math.min(memorySlots, placeableSlots);
|
|
257
|
-
const recommended = Math.min(capacitySlots, memoryBound);
|
|
258
|
-
const limitedBy =
|
|
259
|
-
|
|
393
|
+
const recommended = Math.max(0, Math.min(capacitySlots, memoryBound) - eligibleUnleasedRunning);
|
|
394
|
+
const limitedBy = memoryBound < capacitySlots ? 'memory' : 'capacity';
|
|
395
|
+
if (unleasedRunning > 0) debug.log('pod-lease', 'unleased', { unleasedRunning, running, recommended });
|
|
396
|
+
return { recommended, limitedBy, reason: measure.members.some((m) => m.reason === 'dns') ? 'dns' : null, capacitySlots, memorySlots, liteMemorySlots, placeableSlots, accountSlots, running, pending, unleasedRunning };
|
|
260
397
|
}
|