elanous 0.2.10 → 0.2.11
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/Y4zKzL62sakG5momz3SoQ/_buildManifest.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1159-0c5ef8bc21cb6afb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1394-556de6d71bbdb0fb.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/2310-7a59f043f6db207e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2426.8c6ee0c668d53def.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2548-6e316138d347cefe.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-a39d1d9921e10d43.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/507-5acd98bd6dab2d8a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5445-745eb795c5963d79.js +1 -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/{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/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-ea5bee9dfae512c7.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/app/_not-found/page-d16993889b125c42.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-1dcdc58fc2de2454.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-6a061bd97f0b9398.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-cb95a7432c9ee744.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/{page-54b32bd27726588d.js → page-b12b6d2f8d2d1aea.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-b9efe868bfd36255.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-7c97dcdfe2e37a60.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-8fd36984f549270a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-5721537ca8a3598c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-b654240a572e4c48.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-b9750abc897508c3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-4cd1de371f8ad048.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-d99f89657828421c.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-50cb2a0eb15e6c83.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-c8b1ccda6ba7ae98.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-d6b1d156fb95e74b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-2a9932ddb62d76bd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-9688f5b4f1ff950f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-6b15dd6456c5df09.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/page-35414e0a007b4082.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-e7a529e0507ac86c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-e001823c35c9dd67.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-371423d1a573d64b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-4b764b125339ac17.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-cdf8e4b01067bcf1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-3a24072b35470157.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-94c82eb0ae5abb32.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-0be8d4b2ced75ba0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-d37cb0d52f9b6bd5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-686da0df9bf23e5c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-4dc614f3ce043e63.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-53f80edc6b8dc07c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-d02c782fc792e2af.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-239c17b911b84e72.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-dc6a79fb003f746c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-0fffb84cafdc9da6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-1218753c82d585f6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-4b5935e67531efb5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/page-3285bc87caf1eaa5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-ad553b0e06505ecd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-fca096e3e860306f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-9ebfa78f0d9885c3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-3dc5f21f7e39622d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-c6da2b468454532d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-93bc70e36f595414.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/543f56bcb1f79fca.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/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/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/marketing/measure-site-numbers.ts +169 -0
- package/scripts/measure-gate-shards.ts +309 -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/cut-branch.ts +84 -0
- package/scripts/release-loop/gate-node.ts +14 -13
- package/scripts/release-loop/mac-smoke-node.ts +229 -0
- package/scripts/release-loop/node-verdict.ts +3 -3
- 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 +61 -10
- 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/test-diet/node.ts +1 -1
- package/scripts/tui-regress.ts +2 -2
- package/scripts/ux/demo-rehearsal.ts +243 -0
- package/scripts/ux/lib/rehearsal-verdict.ts +73 -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 +80 -10
- 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/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 +1 -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 +90 -4
- package/src/cli/plugin-cli.ts +31 -22
- package/src/cli/pod-cli.ts +19 -9
- 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 +17 -4
- 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 +48 -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/dashboard/index.ts +9 -4
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +7 -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 +6 -1
- 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 +100 -0
- package/src/field/field-reel.ts +5 -1
- package/src/field-note/index.ts +201 -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 +37 -0
- package/src/graph-runner/graph-grow.ts +47 -0
- package/src/graph-runner/runner.ts +317 -25
- package/src/grok/agent-search.ts +8 -2
- package/src/guardian/secret-exposure-scan.ts +101 -0
- package/src/harness/harness-cli-command.ts +247 -7
- 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/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 +21 -20
- package/src/index.ts +52 -14
- package/src/intake-plane/categorize.ts +18 -46
- package/src/intake-plane/discord-seat-work.ts +10 -0
- 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-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 +26 -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 +36 -0
- 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/outputs-route.ts +44 -0
- 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/trace.ts +18 -6
- package/src/nexus/api/workflow-run-event-bridge.ts +1 -0
- package/src/nexus/api/workflows.ts +80 -6
- package/src/nexus/index.ts +2 -0
- package/src/nexus/kinds/webterm.ts +2 -1
- 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 +116 -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 +109 -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 +2 -2
- package/src/release-loop/feature-store.ts +5 -3
- 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 +548 -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 +38 -33
- package/src/self-dev/draft-triage-rules.ts +8 -5
- 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/gate-baseline.ts +1 -1
- package/src/self-implement/gate-cli.ts +40 -1
- package/src/self-implement/gate-scope.ts +37 -7
- 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 +22 -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 +242 -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 +1 -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 +29 -7
- 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 +214 -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 +10 -1
- package/src/telegram-project-command.ts +71 -0
- package/src/telegram-report.ts +21 -17
- package/src/telegram.ts +29 -3
- package/src/tool-runtime/self-cognition-runtimes.ts +13 -3
- package/src/user-config.ts +102 -20
- 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 → Y4zKzL62sakG5momz3SoQ}/_ssgManifest.js +0 -0
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
import { createHash, randomUUID } from 'node:crypto';
|
|
2
2
|
import { execFile } from 'node:child_process';
|
|
3
|
-
import { existsSync, linkSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmdirSync, statSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { existsSync, linkSync, lstatSync, mkdirSync, readFileSync, readdirSync, readlinkSync, realpathSync, renameSync, rmdirSync, rmSync, statSync, symlinkSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
4
4
|
import { dirname, join, resolve, sep } from 'node:path';
|
|
5
5
|
import { parse as parseYaml, stringify as stringifyYaml } from 'yaml';
|
|
6
6
|
import { getElanousConfigDir, getElanousConfigDirOverride } from '../elanous-config-dir.js';
|
|
7
7
|
import { debug } from '../debug/log.js';
|
|
8
|
+
import { publishInsideEvent } from '../nexus/api/inside-events.js';
|
|
8
9
|
import { elanousStateRoot } from '../autopilot/state-paths.js';
|
|
9
10
|
import { listInstalledPlugins } from '../plugins/install/plugin-install.js';
|
|
10
11
|
import { credentialStatus, pluginEnv } from '../plugins/install/plugin-credentials.js';
|
|
11
12
|
import { effectiveInstanceRoot } from '../instance/resolve.js';
|
|
12
13
|
import { loadNodeCatalog } from '../self-implement/graph-catalog.js';
|
|
13
|
-
import {
|
|
14
|
+
import { proposeGrowth, type GraphGrowth, type GrowthProposer, type GrowthRecipeEffect } from './graph-grow.js';
|
|
15
|
+
import { createLLMGrowthProposer } from './graph-grow-llm.js';
|
|
16
|
+
import { parseGraphTemplateYaml, type GraphEdgeSpec, type GraphNodeSpec, type GraphTemplateSpec } from '../self-implement/graph-yaml.js';
|
|
17
|
+
import { createGraphVariant, type GraphVariantPlan } from '../self-implement/graph-variant.js';
|
|
14
18
|
import { psProcessStartMs, START_TOLERANCE_MS } from '../harness/harness-stop.js';
|
|
15
19
|
import { executeBashNode } from '../workflow-runtime/nodes/bash.js';
|
|
16
20
|
import type { BashNode, NodeExecContext, WorkflowDeps } from '../workflow-runtime/types.js';
|
|
@@ -24,6 +28,8 @@ export interface GraphRunState {
|
|
|
24
28
|
finishedAt?: string;
|
|
25
29
|
pid?: number;
|
|
26
30
|
pidStartedAt?: string;
|
|
31
|
+
activeNode?: { nodeId: string; pid: number; pidStartedAt: string };
|
|
32
|
+
stoppedAt?: string;
|
|
27
33
|
status: 'running' | 'done' | 'failed' | 'budget-exceeded' | 'awaiting-approval';
|
|
28
34
|
path: string[];
|
|
29
35
|
nodes: Array<{ nodeId: string; ok: boolean; exit: number | null; executed: boolean; output?: unknown; error?: string; decidedBy?: string; decidedAt?: string }>;
|
|
@@ -34,6 +40,10 @@ export interface GraphRunState {
|
|
|
34
40
|
sourceHash?: string;
|
|
35
41
|
graphSnapshot?: { graphSha: string; recipesSha: string };
|
|
36
42
|
graphPath?: string;
|
|
43
|
+
variant?: { overlayId: string; goal: string; plan: GraphVariantPlan };
|
|
44
|
+
growth?: GraphGrowth[];
|
|
45
|
+
growthRejections?: Array<{ from: string; outcome: string; reason: string }>;
|
|
46
|
+
growthPark?: { from: string; outcome: string; reason: string };
|
|
37
47
|
resume?: { from: string; at: string; previousStatus: GraphRunState['status']; graph?: 'snapshot' | 'current' };
|
|
38
48
|
executed: number;
|
|
39
49
|
dryRun: boolean;
|
|
@@ -43,12 +53,13 @@ export interface GraphRunState {
|
|
|
43
53
|
export interface GraphRunOptions {
|
|
44
54
|
input?: unknown;
|
|
45
55
|
dryRun?: boolean;
|
|
56
|
+
variant?: { goal: string; plan: GraphVariantPlan };
|
|
46
57
|
runId?: string;
|
|
47
58
|
resumeRunId?: string;
|
|
48
59
|
resumeGraphId?: string;
|
|
49
60
|
fromNodeId?: string;
|
|
50
61
|
useCurrentGraph?: boolean;
|
|
51
|
-
deps?: { root?: string; runBash?: BashRun; log?: (event: string, data: Record<string, unknown>) => void; processStartMs?: (pid: number) => number | null };
|
|
62
|
+
deps?: { root?: string; runBash?: BashRun; log?: (event: string, data: Record<string, unknown>) => void; processStartMs?: (pid: number) => number | null; growthProposer?: GrowthProposer; growthLLM?: (prompt: string) => Promise<string>; classifyGrowthRecipe?: (node: GraphNodeSpec, resolved: { command?: string; approval?: string }) => GrowthRecipeEffect };
|
|
52
63
|
}
|
|
53
64
|
|
|
54
65
|
function safeSegment(value: string): string {
|
|
@@ -115,15 +126,32 @@ function recipesFor(path: string, recipeSource: string): Record<string, Recipe>
|
|
|
115
126
|
return recipes;
|
|
116
127
|
}
|
|
117
128
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
129
|
+
function realRunBash(onSpawn: (pid: number) => boolean): BashRun {
|
|
130
|
+
return (body, opts) => new Promise((resolve, reject) => {
|
|
131
|
+
let unverified: ReturnType<typeof setTimeout> | undefined;
|
|
132
|
+
const child = execFile('/bin/bash', ['-c', body], { cwd: opts.cwd, env: opts.env, timeout: opts.timeoutMs, signal: opts.signal, maxBuffer: 10 * 1024 * 1024 }, (error, stdout, stderr) => {
|
|
133
|
+
if (unverified) clearTimeout(unverified);
|
|
134
|
+
if (error && typeof (error as NodeJS.ErrnoException).code !== 'number') {
|
|
135
|
+
reject(error);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
resolve({ stdout, stderr, exitCode: error ? (error as unknown as { code: number }).code : 0 });
|
|
139
|
+
});
|
|
140
|
+
if (child.pid) {
|
|
141
|
+
try {
|
|
142
|
+
if (!onSpawn(child.pid)) {
|
|
143
|
+
// A process can exit before ps observes it. A completed child is safe to report;
|
|
144
|
+
// an unverified child still running must not be left without a tracked PID.
|
|
145
|
+
unverified = setTimeout(() => {
|
|
146
|
+
if (child.exitCode !== null || child.signalCode !== null) return;
|
|
147
|
+
child.kill('SIGTERM');
|
|
148
|
+
reject(new Error(`cannot verify node process start: ${child.pid}`));
|
|
149
|
+
}, 500);
|
|
150
|
+
}
|
|
151
|
+
} catch (error) { child.kill('SIGTERM'); reject(error); }
|
|
123
152
|
}
|
|
124
|
-
resolve({ stdout, stderr, exitCode: error ? (error as unknown as { code: number }).code : 0 });
|
|
125
153
|
});
|
|
126
|
-
}
|
|
154
|
+
}
|
|
127
155
|
|
|
128
156
|
function graphRunPath(graphId: string, runId: string, root: string): string {
|
|
129
157
|
return join(root, 'graph-runs', safeSegment(graphId), `${safeSegment(runId)}.json`);
|
|
@@ -164,11 +192,77 @@ function persistGraphRun(state: GraphRunState): void {
|
|
|
164
192
|
renameSync(temporary, state.statePath);
|
|
165
193
|
}
|
|
166
194
|
|
|
195
|
+
/** A synchronous, per-ledger critical section shared by runner writes and stop/destroy. */
|
|
196
|
+
function withRunWriteLock<T>(file: string, work: () => T): T {
|
|
197
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
198
|
+
const lock = `${file}.write.lock`;
|
|
199
|
+
const staged = `${lock}.${randomUUID()}.staged`;
|
|
200
|
+
mkdirSync(staged);
|
|
201
|
+
const until = Date.now() + 5_000;
|
|
202
|
+
let acquired = false;
|
|
203
|
+
try {
|
|
204
|
+
writeFileSync(join(staged, 'owner.json'), JSON.stringify({ pid: process.pid, started: Date.now() - process.uptime() * 1_000 }), { flag: 'wx' });
|
|
205
|
+
while (!acquired) {
|
|
206
|
+
try {
|
|
207
|
+
// Unlike mkdir(lock) followed by a write, publication exposes an already complete owner.
|
|
208
|
+
symlinkSync(staged, lock, 'dir');
|
|
209
|
+
acquired = true;
|
|
210
|
+
} catch (error) {
|
|
211
|
+
if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error;
|
|
212
|
+
let owner: { pid: number; started: number } | undefined;
|
|
213
|
+
try { owner = JSON.parse(readFileSync(join(lock, 'owner.json'), 'utf8')); } catch { /* legacy unowned directory */ }
|
|
214
|
+
const ownerStart = owner && Number.isSafeInteger(owner.pid) && Number.isFinite(owner.started) ? psProcessStartMs(owner.pid) : undefined;
|
|
215
|
+
let stale = ownerStart !== undefined && (ownerStart === null || Math.abs(ownerStart - owner!.started) > START_TOLERANCE_MS);
|
|
216
|
+
if (!owner && !lstatSync(lock).isSymbolicLink()) {
|
|
217
|
+
// Legacy mkdir-before-owner crash: do not evict a writer still publishing its owner.
|
|
218
|
+
stale = Date.now() - statSync(lock).mtimeMs >= 5_000;
|
|
219
|
+
}
|
|
220
|
+
if (stale) {
|
|
221
|
+
try {
|
|
222
|
+
if (lstatSync(lock).isSymbolicLink()) {
|
|
223
|
+
const target = readlinkSync(lock);
|
|
224
|
+
if (!target.startsWith(`${lock}.`) || !target.endsWith('.staged')) throw new Error('invalid graph run write lock');
|
|
225
|
+
unlinkSync(lock);
|
|
226
|
+
rmSync(target, { recursive: true, force: true });
|
|
227
|
+
} else {
|
|
228
|
+
if (owner) unlinkSync(join(lock, 'owner.json'));
|
|
229
|
+
rmdirSync(lock);
|
|
230
|
+
}
|
|
231
|
+
} catch (cause) {
|
|
232
|
+
if ((cause as NodeJS.ErrnoException).code !== 'ENOENT') throw cause;
|
|
233
|
+
}
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
if (Date.now() >= until) throw new Error(`graph run write lock is held: ${file}`);
|
|
237
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 10);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return work();
|
|
241
|
+
} finally {
|
|
242
|
+
if (acquired && existsSync(lock) && lstatSync(lock).isSymbolicLink() && readlinkSync(lock) === staged) unlinkSync(lock);
|
|
243
|
+
rmSync(staged, { recursive: true, force: true });
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function persistOwnedRun(state: GraphRunState, expected: Pick<GraphRunState, 'pid' | 'pidStartedAt' | 'stoppedAt'>, allowRestart = false): void {
|
|
248
|
+
withRunWriteLock(state.statePath, () => {
|
|
249
|
+
let disk: GraphRunState;
|
|
250
|
+
try { disk = JSON.parse(readFileSync(state.statePath, 'utf8')) as GraphRunState; }
|
|
251
|
+
catch (cause) { throw new Error(`run was removed or unreadable: ${state.graphId}/${state.runId}`, { cause }); }
|
|
252
|
+
if (disk.graphId !== state.graphId || disk.runId !== state.runId || disk.pid !== expected.pid ||
|
|
253
|
+
disk.pidStartedAt !== expected.pidStartedAt || disk.stoppedAt !== expected.stoppedAt ||
|
|
254
|
+
(disk.stoppedAt !== undefined && !allowRestart)) {
|
|
255
|
+
throw new Error(`run was stopped or ownership changed: ${state.graphId}/${state.runId}`);
|
|
256
|
+
}
|
|
257
|
+
persistGraphRun(state);
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
|
|
167
261
|
export interface ExpectedGraphApprovalVisit { nodeId: string; visit: number }
|
|
168
262
|
|
|
169
263
|
export function decideGraphApproval(graphId: string, runId: string, decision: 'approved' | 'rejected', by?: string, root = effectiveInstanceRoot(), expected?: ExpectedGraphApprovalVisit): GraphRunState {
|
|
170
264
|
const state = readGraphRun(graphId, runId, root);
|
|
171
|
-
if (state.status !== 'awaiting-approval' || !state.pending || state.pending.decision ||
|
|
265
|
+
if (state.status !== 'awaiting-approval' || !state.pending || state.growthPark || state.pending.decision ||
|
|
172
266
|
(expected && (state.dryRun || state.pending.nodeId !== expected.nodeId || state.path.at(-1) !== expected.nodeId ||
|
|
173
267
|
!Number.isSafeInteger(expected.visit) || expected.visit < 1 ||
|
|
174
268
|
state.path.filter(node => node === expected.nodeId).length !== expected.visit))) {
|
|
@@ -208,6 +302,22 @@ export function lastJsonObject(stdout: unknown): Record<string, unknown> | undef
|
|
|
208
302
|
}
|
|
209
303
|
}
|
|
210
304
|
|
|
305
|
+
/** Reconstruct the run-local edges from the ledger without rewriting the YAML snapshot. */
|
|
306
|
+
function withGrowth(graph: GraphTemplateSpec, growth: readonly GraphGrowth[]): GraphTemplateSpec {
|
|
307
|
+
return growth.reduce<GraphTemplateSpec>((current, item) => {
|
|
308
|
+
const edge = current.edges.find((candidate) => candidate.from === item.undo.removeOutcome.from);
|
|
309
|
+
if (!edge || !edge.map || !item.node || item.undo.removeNode !== item.node.nodeId ||
|
|
310
|
+
current.nodes.some((node) => node.nodeId === item.node.nodeId) ||
|
|
311
|
+
Object.hasOwn(edge.map, item.undo.removeOutcome.outcome) ||
|
|
312
|
+
!item.edges.some((added) => added.from === edge.from && added.map?.[item.undo.removeOutcome.outcome] === item.node.nodeId)) {
|
|
313
|
+
throw new Error('invalid saved graph growth');
|
|
314
|
+
}
|
|
315
|
+
return { ...current, nodes: [...current.nodes, item.node], edges: current.edges.map((candidate) => candidate === edge
|
|
316
|
+
? { ...edge, map: { ...edge.map, [item.undo.removeOutcome.outcome]: item.node.nodeId } } : candidate)
|
|
317
|
+
.concat(item.undo.removeEdge ? [item.undo.removeEdge] : []) };
|
|
318
|
+
}, graph);
|
|
319
|
+
}
|
|
320
|
+
|
|
211
321
|
/** 이름 붙은 결과: 산출의 `outcome` 이 그 간선 map 의 키면 그 목적지 — 아니면 종전 ok/fail(exit) → fallback. */
|
|
212
322
|
function nextNode(edges: readonly GraphEdgeSpec[], nodeId: string, exitOutcome: 'ok' | 'fail', namedOutcome?: string): string | undefined {
|
|
213
323
|
const edge = edges.find((e) => e.from === nodeId);
|
|
@@ -222,6 +332,7 @@ export async function runGraph(path: string, options: GraphRunOptions = {}): Pro
|
|
|
222
332
|
if (options.resumeRunId && options.runId) throw new Error('runId and resumeRunId cannot be combined');
|
|
223
333
|
if (options.fromNodeId && !options.resumeRunId) throw new Error('--from requires --resume');
|
|
224
334
|
if (options.useCurrentGraph && !options.resumeRunId) throw new Error('--use-current-graph requires --resume');
|
|
335
|
+
if (options.variant && options.resumeRunId) throw new Error('variant cannot be combined with --resume');
|
|
225
336
|
const root = options.deps?.root ?? effectiveInstanceRoot();
|
|
226
337
|
const matchingRuns = options.resumeRunId && !options.resumeGraphId
|
|
227
338
|
? listGraphRuns(root).runs.filter((run) => run.runId === options.resumeRunId && run.graphPath === resolve(path)) : [];
|
|
@@ -250,7 +361,22 @@ export async function runGraph(path: string, options: GraphRunOptions = {}): Pro
|
|
|
250
361
|
}
|
|
251
362
|
const parsed = parseGraphTemplateYaml(stringifyYaml(document), path);
|
|
252
363
|
if (!parsed.template || parsed.errors.length) throw new Error(parsed.errors.map((e) => `${e.path}: ${e.message}`).join('\n'));
|
|
253
|
-
|
|
364
|
+
let graph = parsed.template;
|
|
365
|
+
const variant = options.variant ? createGraphVariant({ template: graph, ...options.variant }) : undefined;
|
|
366
|
+
if (variant && !variant.ok) throw new Error(`graph variant rejected: ${JSON.stringify(variant.rejections)}`);
|
|
367
|
+
if (variant?.ok) graph = variant.template;
|
|
368
|
+
const variantSource = variant?.ok ? stringifyYaml({ ...document,
|
|
369
|
+
nodes: graph.nodes.map((node, index) => ({
|
|
370
|
+
...((document.nodes as Record<string, unknown>[])[index] ?? {}), node_id: node.nodeId, kind: node.kind,
|
|
371
|
+
recipe: node.recipe, max_visits: node.maxVisits,
|
|
372
|
+
...(node.contract ? { contract: node.contract } : {}),
|
|
373
|
+
})),
|
|
374
|
+
edges: graph.edges.map((edge, index) => ({
|
|
375
|
+
...((document.edges as Record<string, unknown>[])[index] ?? {}), from: edge.from,
|
|
376
|
+
...(edge.to === undefined ? { on: edge.on, map: edge.map } : { to: edge.to }),
|
|
377
|
+
...(edge.fallback ? { fallback: edge.fallback } : {}),
|
|
378
|
+
})),
|
|
379
|
+
}) : source;
|
|
254
380
|
const pluginRoot = elanousStateRoot();
|
|
255
381
|
const graphFile = graphMode === 'snapshot' && !existsSync(path) ? resolve(path) : realpathSync(path);
|
|
256
382
|
const installed = listInstalledPlugins(pluginRoot);
|
|
@@ -276,7 +402,7 @@ export async function runGraph(path: string, options: GraphRunOptions = {}): Pro
|
|
|
276
402
|
const recipes = recipesFor(path, recipeSource);
|
|
277
403
|
const catalogRoles = new Set(loadNodeCatalog().roles.keys());
|
|
278
404
|
// Bind a human decision to the exact graph and recipe contents, not just its id or approval prompt.
|
|
279
|
-
const approvalSourceHash = createHash('sha256').update(
|
|
405
|
+
const approvalSourceHash = createHash('sha256').update(variantSource).update('\0').update(recipeSource).digest('hex');
|
|
280
406
|
// Resolve all commands before executing any node; an unknown recipe must never produce a partial publication.
|
|
281
407
|
for (const node of graph.nodes) {
|
|
282
408
|
if (node.recipe === 'none') continue;
|
|
@@ -307,12 +433,17 @@ export async function runGraph(path: string, options: GraphRunOptions = {}): Pro
|
|
|
307
433
|
const state: GraphRunState = options.resumeRunId
|
|
308
434
|
? readGraphRun(graphId, runId, root)
|
|
309
435
|
: { graphId, runId, startedAt: new Date().toISOString(), ...runnerOwner, status: 'running', path: [], nodes: [], sourceHash: approvalSourceHash,
|
|
310
|
-
graphSnapshot: { graphSha: createHash('sha256').update(
|
|
311
|
-
graphPath: resolve(path), ...(
|
|
436
|
+
graphSnapshot: { graphSha: createHash('sha256').update(variantSource).digest('hex'), recipesSha: createHash('sha256').update(recipeSource).digest('hex') },
|
|
437
|
+
graphPath: resolve(path), ...(variant?.ok ? { variant: { overlayId: variant.overlay.overlayId, ...options.variant! } } : {}),
|
|
438
|
+
...(options.input === undefined ? {} : { input: options.input }), executed: 0, dryRun: options.dryRun === true, statePath };
|
|
312
439
|
if (options.resumeRunId && graphMode === 'snapshot' && state.sourceHash && state.sourceHash !== approvalSourceHash) {
|
|
313
440
|
throw new Error('graph snapshot changed since run started');
|
|
314
441
|
}
|
|
315
442
|
if (options.resumeRunId && options.dryRun !== undefined && options.dryRun !== state.dryRun) throw new Error('cannot change dryRun when resuming');
|
|
443
|
+
if (options.resumeRunId && state.growth?.length && graphMode === 'current' && state.sourceHash !== approvalSourceHash) {
|
|
444
|
+
throw new Error('graph or recipes changed since grown run');
|
|
445
|
+
}
|
|
446
|
+
if (state.growth?.length) graph = withGrowth(graph, state.growth);
|
|
316
447
|
if (options.fromNodeId) {
|
|
317
448
|
if (state.status !== 'failed' || state.pending || state.dryRun) throw new Error('--from requires a failed, non-dry run without pending approval');
|
|
318
449
|
if (graphMode === 'current' && (!state.sourceHash || state.sourceHash !== approvalSourceHash)) throw new Error('graph or recipes changed since failed run');
|
|
@@ -353,10 +484,14 @@ export async function runGraph(path: string, options: GraphRunOptions = {}): Pro
|
|
|
353
484
|
state.nodes = state.nodes.slice(0, from);
|
|
354
485
|
state.executed = state.nodes.filter((node) => node.executed).length;
|
|
355
486
|
state.status = 'running';
|
|
487
|
+
delete state.stoppedAt;
|
|
356
488
|
Object.assign(state, runnerOwner);
|
|
357
489
|
state.resume = { from: options.fromNodeId, at: new Date().toISOString(), previousStatus: 'failed', graph: graphMode };
|
|
358
490
|
// Persist the restart boundary before executing it, so previous outputs remain durable.
|
|
359
|
-
|
|
491
|
+
persistOwnedRun(state, saved!, true);
|
|
492
|
+
}
|
|
493
|
+
if (options.resumeRunId && state.growthPark) {
|
|
494
|
+
throw new Error(`growth is parked for human confirmation: ${graphId}/${runId}`);
|
|
360
495
|
}
|
|
361
496
|
if (options.resumeRunId && state.status === 'running' && state.pending) {
|
|
362
497
|
throw new Error(`run is not awaiting approval: ${graphId}/${runId}`);
|
|
@@ -377,12 +512,12 @@ export async function runGraph(path: string, options: GraphRunOptions = {}): Pro
|
|
|
377
512
|
const previousStatus = state.status;
|
|
378
513
|
Object.assign(state, runnerOwner);
|
|
379
514
|
state.resume = { from: state.pending?.nodeId ?? state.path.at(-1) ?? graph.entryNode, at: new Date().toISOString(), previousStatus, graph: graphMode };
|
|
380
|
-
|
|
515
|
+
persistOwnedRun(state, saved!);
|
|
381
516
|
}
|
|
382
517
|
if (options.resumeRunId) debug.log('graph.runs', 'resume', { graphId, runId, from: state.resume?.from, graph: graphMode });
|
|
383
518
|
const persist = () => {
|
|
384
519
|
if (state.status === 'done' || state.status === 'failed' || state.status === 'budget-exceeded') state.finishedAt ??= new Date().toISOString();
|
|
385
|
-
|
|
520
|
+
persistOwnedRun(state, runnerOwner);
|
|
386
521
|
};
|
|
387
522
|
const log = options.deps?.log ?? ((event: string, data: Record<string, unknown>) => debug.log('graph.runner', event, data));
|
|
388
523
|
const visits = new Map<string, number>();
|
|
@@ -399,15 +534,16 @@ export async function runGraph(path: string, options: GraphRunOptions = {}): Pro
|
|
|
399
534
|
throw error;
|
|
400
535
|
}
|
|
401
536
|
mkdirSync(`${statePath}.graph`);
|
|
402
|
-
writeFileSync(join(`${statePath}.graph`, 'graph.yaml'),
|
|
537
|
+
writeFileSync(join(`${statePath}.graph`, 'graph.yaml'), variantSource, { flag: 'wx' });
|
|
403
538
|
writeFileSync(join(`${statePath}.graph`, 'recipes.yaml'), recipeSource, { flag: 'wx' });
|
|
404
539
|
}
|
|
405
540
|
while (current !== undefined) {
|
|
406
541
|
const node = graph.nodes.find((n) => n.nodeId === current);
|
|
407
542
|
if (!node) throw new Error(`undeclared node: ${current}`);
|
|
408
543
|
const resumingPending = state.pending?.nodeId === current;
|
|
409
|
-
const completed =
|
|
410
|
-
const resumingCompleted = options.resumeRunId !== undefined && !options.fromNodeId &&
|
|
544
|
+
const completed = state.nodes.at(-1);
|
|
545
|
+
const resumingCompleted = options.resumeRunId !== undefined && !options.fromNodeId && completed?.nodeId === current &&
|
|
546
|
+
state.path.at(-1) === current && !resumingPending;
|
|
411
547
|
if (!resumingPending && !resumingCompleted && (visits.get(current) ?? 0) >= node.maxVisits) {
|
|
412
548
|
state.status = 'budget-exceeded';
|
|
413
549
|
log('budget-exceeded', { graphId, runId, nodeId: current });
|
|
@@ -447,6 +583,7 @@ export async function runGraph(path: string, options: GraphRunOptions = {}): Pro
|
|
|
447
583
|
const startedAt = performance.now();
|
|
448
584
|
console.error(`[graph] ${current} start (0.00s)`);
|
|
449
585
|
debug.log('graph.run', 'node', { graphId, runId, nodeId: current, phase: 'start', dryRun: state.dryRun });
|
|
586
|
+
publishInsideEvent({ kind: 'node', graphId, runId, nodeId: current, phase: 'start' });
|
|
450
587
|
log('node-start', { graphId, runId, nodeId: current, visit: visits.get(current), dryRun: state.dryRun });
|
|
451
588
|
persist();
|
|
452
589
|
let ok = approval && !state.dryRun ? state.pending?.decision === 'approved' : true;
|
|
@@ -464,9 +601,27 @@ export async function runGraph(path: string, options: GraphRunOptions = {}): Pro
|
|
|
464
601
|
const redactCredentials = (text: string) => Object.values(credentials).filter(value => value.length > 0)
|
|
465
602
|
.reduce((safe, value) => safe.replaceAll(value, '[REDACTED]'), text);
|
|
466
603
|
const runBash: BashRun = async (body, opts) => {
|
|
467
|
-
const
|
|
468
|
-
|
|
469
|
-
|
|
604
|
+
const execute = options.deps?.runBash ?? realRunBash((pid) => {
|
|
605
|
+
// Publish the child before its start time is known, so a concurrent stop refuses instead of failing
|
|
606
|
+
// the ledger while an untracked node keeps working (REL8b review round 3).
|
|
607
|
+
state.activeNode = { nodeId: current!, pid, pidStartedAt: UNVERIFIED_NODE_START };
|
|
608
|
+
persist();
|
|
609
|
+
const started = (options.deps?.processStartMs ?? psProcessStartMs)(pid);
|
|
610
|
+
if (started === null) return false;
|
|
611
|
+
state.activeNode = { nodeId: current!, pid, pidStartedAt: new Date(started).toISOString() };
|
|
612
|
+
persist();
|
|
613
|
+
return true;
|
|
614
|
+
});
|
|
615
|
+
try {
|
|
616
|
+
const result = await execute(body, opts);
|
|
617
|
+
code = result.exitCode;
|
|
618
|
+
return { ...result, stdout: redactCredentials(result.stdout), stderr: redactCredentials(result.stderr) };
|
|
619
|
+
} finally {
|
|
620
|
+
if (state.activeNode?.nodeId === current) {
|
|
621
|
+
delete state.activeNode;
|
|
622
|
+
persist();
|
|
623
|
+
}
|
|
624
|
+
}
|
|
470
625
|
};
|
|
471
626
|
if (owner) debug.log('plugin.credentials', 'injected', { plugin: owner.name, fields: credentialFields, count: credentialFields.length });
|
|
472
627
|
const env = { ...process.env };
|
|
@@ -511,6 +666,8 @@ export async function runGraph(path: string, options: GraphRunOptions = {}): Pro
|
|
|
511
666
|
const seconds = Number(((performance.now() - startedAt) / 1000).toFixed(2));
|
|
512
667
|
console.error(`[graph] ${current} ${ok ? 'ok' : 'fail'} (${seconds.toFixed(2)}s)`);
|
|
513
668
|
debug.log('graph.run', 'node', { graphId, runId, nodeId: current, phase: ok ? 'ok' : 'fail', seconds, exit });
|
|
669
|
+
publishInsideEvent({ kind: 'node', graphId, runId, nodeId: current, phase: ok ? 'ok' : 'fail', seconds });
|
|
670
|
+
publishInsideEvent({ kind: 'verdict', graphId, runId, nodeId: current, verdict: ok ? 'ok' : 'fail', ...(namedOutcome === undefined ? {} : { outcome: namedOutcome }) });
|
|
514
671
|
log('node-end', { graphId, runId, nodeId: current, ok, exit, ...(namedOutcome === undefined ? {} : { outcome: namedOutcome }), dryRun: state.dryRun });
|
|
515
672
|
persist();
|
|
516
673
|
if (graph.terminalNodes.includes(current)) {
|
|
@@ -518,7 +675,42 @@ export async function runGraph(path: string, options: GraphRunOptions = {}): Pro
|
|
|
518
675
|
persist();
|
|
519
676
|
break;
|
|
520
677
|
}
|
|
678
|
+
const fromNodeId = current;
|
|
679
|
+
const edge = graph.edges.find((candidate) => candidate.from === current);
|
|
680
|
+
if (document.grow === 'on' && !state.dryRun && namedOutcome !== undefined &&
|
|
681
|
+
edge?.on === 'outcome' && edge.map && !Object.hasOwn(edge.map, namedOutcome) &&
|
|
682
|
+
!state.growth?.some((item) => item.undo.removeOutcome.from === current && item.undo.removeOutcome.outcome === namedOutcome) &&
|
|
683
|
+
!state.growthRejections?.some((item) => item.from === current && item.outcome === namedOutcome)) {
|
|
684
|
+
const result = (state.growth?.length ?? 0) >= 3 ? { ok: false as const, reason: 'growth limit 3 reached' }
|
|
685
|
+
: await proposeGrowth({ graph, nodeId: current, outcome: namedOutcome, runId, output: state.nodes.at(-1)?.output },
|
|
686
|
+
options.deps?.growthProposer ?? createLLMGrowthProposer(options.deps?.growthLLM), (candidate) => {
|
|
687
|
+
const resolved = resolveNodeRecipe(candidate, recipes, catalogRoles);
|
|
688
|
+
if (resolved.approval || (!resolved.command && candidate.recipe !== 'none')) return 'unknown';
|
|
689
|
+
return options.deps?.classifyGrowthRecipe?.(candidate, { command: resolved.command?.command }) ?? 'unknown';
|
|
690
|
+
});
|
|
691
|
+
if (result.ok) {
|
|
692
|
+
graph = result.graph;
|
|
693
|
+
state.growth ??= [];
|
|
694
|
+
state.growth.push(result.growth);
|
|
695
|
+
persist();
|
|
696
|
+
log('edge-added', { graphId, runId, from: current, outcome: namedOutcome, to: result.growth.node.nodeId });
|
|
697
|
+
debug.log('graph.run', 'edge-added', { graphId, runId, from: current, outcome: namedOutcome, to: result.growth.node.nodeId });
|
|
698
|
+
publishInsideEvent({ kind: 'edge-added', graphId, runId, from: current, outcome: namedOutcome, to: result.growth.node.nodeId });
|
|
699
|
+
} else {
|
|
700
|
+
state.growthRejections ??= [];
|
|
701
|
+
state.growthRejections.push({ from: current, outcome: namedOutcome, reason: result.reason });
|
|
702
|
+
if (result.park) {
|
|
703
|
+
state.status = 'awaiting-approval';
|
|
704
|
+
state.pending = { nodeId: current, message: result.reason, since: new Date().toISOString() };
|
|
705
|
+
state.growthPark = { from: current, outcome: namedOutcome, reason: result.reason };
|
|
706
|
+
persist();
|
|
707
|
+
break;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
persist();
|
|
711
|
+
}
|
|
521
712
|
current = nextNode(graph.edges, current, state.dryRun ? 'ok' : ok ? 'ok' : 'fail', state.dryRun ? undefined : namedOutcome);
|
|
713
|
+
if (current !== undefined) publishInsideEvent({ kind: 'edge', graphId, runId, from: fromNodeId, to: current });
|
|
522
714
|
if (current === undefined) {
|
|
523
715
|
state.status = 'failed';
|
|
524
716
|
persist();
|
|
@@ -526,12 +718,112 @@ export async function runGraph(path: string, options: GraphRunOptions = {}): Pro
|
|
|
526
718
|
}
|
|
527
719
|
return state;
|
|
528
720
|
} finally {
|
|
529
|
-
if (options.resumeRunId) rmdirSync(resumeLock);
|
|
721
|
+
if (options.resumeRunId && existsSync(resumeLock)) rmdirSync(resumeLock);
|
|
530
722
|
}
|
|
531
723
|
}
|
|
532
724
|
|
|
533
725
|
export type GraphRunAlive = boolean | 'unknown';
|
|
534
726
|
|
|
727
|
+
/** activeNode.pidStartedAt while the node child is spawned but its start time is not read yet — stop must not pass it. */
|
|
728
|
+
const UNVERIFIED_NODE_START = 'unverified';
|
|
729
|
+
/** A failed run's empty resume lock younger than this may belong to a resume that has not persisted «running» yet. */
|
|
730
|
+
const RESUME_LOCK_GRACE_MS = 60_000;
|
|
731
|
+
|
|
732
|
+
function runArtifacts(state: GraphRunState): string[] {
|
|
733
|
+
const file = state.statePath;
|
|
734
|
+
const claims = readdirSync(dirname(file))
|
|
735
|
+
.filter(name => name.startsWith(`${state.runId}.json.`) && /^\d+\.decision\.json$/.test(name.slice(`${state.runId}.json.`.length)))
|
|
736
|
+
.map(name => join(dirname(file), name));
|
|
737
|
+
for (const claim of claims) {
|
|
738
|
+
if (lstatSync(claim).isSymbolicLink()) throw new Error('graph run artifact is a symlink');
|
|
739
|
+
let value: unknown;
|
|
740
|
+
try { value = JSON.parse(readFileSync(claim, 'utf8')); }
|
|
741
|
+
catch { throw new Error(`invalid graph run decision claim: ${claim}`); }
|
|
742
|
+
const visit = Number(claim.slice(`${file}.`.length, -'.decision.json'.length));
|
|
743
|
+
if (!isDecisionClaim(value) || !Number.isSafeInteger(visit) || visit < 1 ||
|
|
744
|
+
(value as { nodeId: string }).nodeId !== state.path[visit - 1]) {
|
|
745
|
+
throw new Error(`invalid graph run decision claim: ${claim}`);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
return [file, `${file}.graph`, `${file}.contexts`, ...claims];
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
/** Manage only an identity-checked ledger in this instance, never a caller-supplied path. */
|
|
752
|
+
export function manageGraphRun(graphId: string, runId: string, action: 'stop' | 'destroy',
|
|
753
|
+
root = effectiveInstanceRoot(), processStartMs: (pid: number) => number | null = psProcessStartMs,
|
|
754
|
+
signal: (pid: number) => void = (pid) => process.kill(pid, 'SIGTERM')): GraphRunState {
|
|
755
|
+
const file = graphRunPath(safeSegment(graphId), safeSegment(runId), root);
|
|
756
|
+
return withRunWriteLock(file, () => {
|
|
757
|
+
if (lstatSync(join(root, 'graph-runs')).isSymbolicLink() || lstatSync(dirname(file)).isSymbolicLink() || lstatSync(file).isSymbolicLink()) throw new Error('graph run path is a symlink');
|
|
758
|
+
const state = readGraphRun(graphId, runId, root);
|
|
759
|
+
if (state.statePath !== file || !Array.isArray(state.path) || !Array.isArray(state.nodes) ||
|
|
760
|
+
!Number.isSafeInteger(state.executed) || state.executed < 0 || (state.status === 'running' && state.pending)) {
|
|
761
|
+
throw new Error(`invalid run state: ${graphId}/${runId}`);
|
|
762
|
+
}
|
|
763
|
+
for (const path of runArtifacts(state)) {
|
|
764
|
+
try { if (lstatSync(path).isSymbolicLink()) throw new Error('graph run artifact is a symlink'); }
|
|
765
|
+
catch (error) { if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error; }
|
|
766
|
+
}
|
|
767
|
+
const live = graphRunAlive(state, processStartMs);
|
|
768
|
+
if (action === 'stop' && state.status !== 'running') throw new Error(`run is not running: ${graphId}/${runId}`);
|
|
769
|
+
const lock = `${file}.resume.lock`;
|
|
770
|
+
if (existsSync(lock) && (!lstatSync(lock).isDirectory() || readdirSync(lock).length > 0)) throw new Error('invalid resume lock');
|
|
771
|
+
if (existsSync(lock) && state.status !== 'running') {
|
|
772
|
+
// A resume persists «running» right after taking the lock; an older empty lock on a finished run is a crash leftover.
|
|
773
|
+
const ageMs = Date.now() - statSync(lock).mtimeMs;
|
|
774
|
+
if (ageMs < RESUME_LOCK_GRACE_MS) throw new Error(`run is being resumed: ${graphId}/${runId}`);
|
|
775
|
+
rmdirSync(lock);
|
|
776
|
+
debug.log('graph.runs', 'stale-resume-lock-cleared', { graphId, runId, ageMs: Math.round(ageMs), status: state.status });
|
|
777
|
+
}
|
|
778
|
+
if (state.status === 'running') {
|
|
779
|
+
if (live === 'unknown') throw new Error(`cannot verify run owner: ${graphId}/${runId}`);
|
|
780
|
+
if (live && state.pid === process.pid) throw new Error('cannot stop the current graph runner');
|
|
781
|
+
const node = state.activeNode;
|
|
782
|
+
if (node && (node.nodeId !== state.path.at(-1) || state.path.length !== state.nodes.length + 1)) throw new Error('active node does not match saved path');
|
|
783
|
+
if (state.path.length !== state.nodes.length && state.path.length !== state.nodes.length + 1) {
|
|
784
|
+
throw new Error('saved run path and node records do not match');
|
|
785
|
+
}
|
|
786
|
+
if (state.path.some((nodeId, i) => state.nodes[i] && state.nodes[i]?.nodeId !== nodeId) ||
|
|
787
|
+
state.executed !== state.nodes.filter(record => record.executed).length) throw new Error('saved run path and node records do not match');
|
|
788
|
+
if (node?.pidStartedAt === UNVERIFIED_NODE_START) {
|
|
789
|
+
// While the runner lives it will either verify this child or kill it; retry stop after that.
|
|
790
|
+
if (live) throw new Error(`node process start is not verified yet — retry stop: ${graphId}/${runId}`);
|
|
791
|
+
// Runner gone (e.g. reboot): the pid may be reused, so never signal an identity we could not verify.
|
|
792
|
+
debug.log('graph.runs', 'unverified-node-not-signaled', { graphId, runId, nodeId: node.nodeId, pid: node.pid });
|
|
793
|
+
} else if (node) {
|
|
794
|
+
const nodeAlive = graphRunAlive({ ...state, pid: node.pid, pidStartedAt: node.pidStartedAt }, processStartMs);
|
|
795
|
+
if (nodeAlive === 'unknown') throw new Error('cannot verify node process owner');
|
|
796
|
+
if (nodeAlive && node.pid === process.pid) throw new Error('cannot stop the current process');
|
|
797
|
+
if (nodeAlive && node.pid !== state.pid) {
|
|
798
|
+
try { signal(node.pid); }
|
|
799
|
+
catch (error) { if ((error as NodeJS.ErrnoException).code !== 'ESRCH') throw error; }
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
if (state.path.length === state.nodes.length + 1) {
|
|
803
|
+
state.nodes.push({ nodeId: state.path.at(-1)!, ok: false, exit: null, executed: false, error: 'stopped before node completed' });
|
|
804
|
+
}
|
|
805
|
+
state.status = 'failed';
|
|
806
|
+
state.finishedAt = new Date().toISOString();
|
|
807
|
+
state.stoppedAt = state.finishedAt;
|
|
808
|
+
delete state.activeNode;
|
|
809
|
+
persistGraphRun(state);
|
|
810
|
+
try {
|
|
811
|
+
if (live) signal(state.pid!);
|
|
812
|
+
} catch (error) {
|
|
813
|
+
if ((error as NodeJS.ErrnoException).code !== 'ESRCH') throw error;
|
|
814
|
+
} finally {
|
|
815
|
+
// The failed ledger is durable even if its runner vanished between verification and signaling.
|
|
816
|
+
if (existsSync(lock)) rmdirSync(lock);
|
|
817
|
+
}
|
|
818
|
+
} else if (existsSync(lock)) rmdirSync(lock);
|
|
819
|
+
if (action === 'destroy') {
|
|
820
|
+
for (const path of runArtifacts(state)) rmSync(path, { recursive: true, force: true });
|
|
821
|
+
}
|
|
822
|
+
debug.log('graph.runs', action, { graphId, runId, previousAlive: live, status: state.status });
|
|
823
|
+
return state;
|
|
824
|
+
});
|
|
825
|
+
}
|
|
826
|
+
|
|
535
827
|
export function graphRunAlive(state: GraphRunState, processStartMs: (pid: number) => number | null = psProcessStartMs): GraphRunAlive {
|
|
536
828
|
if (state.pid === undefined) return 'unknown';
|
|
537
829
|
if (!Number.isSafeInteger(state.pid) || state.pid <= 0 || typeof state.pidStartedAt !== 'string') return false;
|
package/src/grok/agent-search.ts
CHANGED
|
@@ -71,6 +71,8 @@ export interface GrokAgentSearchResult {
|
|
|
71
71
|
/** Deduped source URLs, in first-seen order (annotations first, then
|
|
72
72
|
* raw search_call sources). */
|
|
73
73
|
citations: GrokCitation[];
|
|
74
|
+
/** URLs supplied by an actual x_search_call (not merely web citations). */
|
|
75
|
+
xSearchSources?: string[];
|
|
74
76
|
/** Set when ok=false — a short diagnostic ('missing-api-key',
|
|
75
77
|
* 'grok responses 4xx: …', 'threw: …'). */
|
|
76
78
|
error?: string;
|
|
@@ -151,6 +153,7 @@ export function parseResponsesOutput(body: { output?: unknown[] } | null): GrokA
|
|
|
151
153
|
const output = Array.isArray(body?.output) ? body!.output! : [];
|
|
152
154
|
let text = '';
|
|
153
155
|
const citations: GrokCitation[] = [];
|
|
156
|
+
const xSearchSources = new Set<string>();
|
|
154
157
|
const seen = new Set<string>();
|
|
155
158
|
const addCite = (url?: unknown, title?: unknown): void => {
|
|
156
159
|
if (typeof url !== 'string' || !url || seen.has(url)) return;
|
|
@@ -179,9 +182,12 @@ export function parseResponsesOutput(body: { output?: unknown[] } | null): GrokA
|
|
|
179
182
|
if ((item?.type === 'web_search_call' || item?.type === 'x_search_call')) {
|
|
180
183
|
const action = item.action as Record<string, unknown> | undefined;
|
|
181
184
|
const sources = Array.isArray(action?.sources) ? action!.sources as Array<Record<string, unknown>> : [];
|
|
182
|
-
for (const s of sources)
|
|
185
|
+
for (const s of sources) {
|
|
186
|
+
addCite(s?.url, s?.title);
|
|
187
|
+
if (item.type === 'x_search_call' && typeof s?.url === 'string') xSearchSources.add(s.url);
|
|
188
|
+
}
|
|
183
189
|
}
|
|
184
190
|
}
|
|
185
191
|
|
|
186
|
-
return { ok: true, status: 200, text: text.trim(), citations };
|
|
192
|
+
return { ok: true, status: 200, text: text.trim(), citations, xSearchSources: [...xSearchSources] };
|
|
187
193
|
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { elanousStateRoot } from '../autopilot/state-paths.js';
|
|
4
|
+
import { listCoordEvents, type CoordEvent } from '../context-bus/coord-events.js';
|
|
5
|
+
import { DecisionLedger } from '../decisions/decision-ledger.js';
|
|
6
|
+
import { LogStore } from '../mss/logging/log-store.js';
|
|
7
|
+
|
|
8
|
+
export type SecretKind = 'token-prefix' | 'key' | 'bearer' | 'private-key';
|
|
9
|
+
export interface SecretCandidate { store: 'logs' | 'decisions' | 'context'; rowId: string; kind: SecretKind; prefix: string; length: number }
|
|
10
|
+
export interface SecretScanResult { mode: 'shadow' | 'publish'; candidates: SecretCandidate[]; raised: number }
|
|
11
|
+
|
|
12
|
+
const PATTERNS: Array<{ kind: SecretKind; pattern: RegExp }> = [
|
|
13
|
+
{ kind: 'private-key', pattern: /-----BEGIN (?:[A-Z0-9_-]+ )?PRIVATE KEY(?: BLOCK)?-----/g },
|
|
14
|
+
{ kind: 'bearer', pattern: /\bBearer\s+([A-Za-z0-9._~+/-]{12,}={0,2})/gi },
|
|
15
|
+
{ kind: 'token-prefix', pattern: /\b(?:sk-[A-Za-z0-9_-]{16,}|xai-[A-Za-z0-9_-]{20,}|gh[pousr]_[A-Za-z0-9_]{12,}|github_pat_[A-Za-z0-9_]{12,}|xox[baprs]-[A-Za-z0-9-]{12,}|(?:AKIA|ASIA|ABIA|ACCA)[A-Z2-7]{16}|AIza[A-Za-z0-9_-]{35})\b/g },
|
|
16
|
+
{ kind: 'key', pattern: /\b(?:[A-Za-z][A-Za-z0-9_]*_)?(?:api[_-]?key|secret[_-]?key|access[_-]?key|token)(?:\\?["'])?\s*[=:]\s*(?:\\?["'])?([A-Za-z0-9_./+~-]{16,}={0,2})/gi },
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
function scanText(store: SecretCandidate['store'], rowId: string, text: string): SecretCandidate[] {
|
|
20
|
+
const found: SecretCandidate[] = [];
|
|
21
|
+
const spans: Array<[number, number]> = [];
|
|
22
|
+
for (const { kind, pattern } of PATTERNS) {
|
|
23
|
+
for (const match of text.matchAll(pattern)) {
|
|
24
|
+
const value = match[1] ?? match[0];
|
|
25
|
+
const start = match.index! + (match[1] ? match[0].lastIndexOf(value) : 0);
|
|
26
|
+
const end = start + value.length;
|
|
27
|
+
if (spans.some(([a, b]) => start < b && end > a)) continue;
|
|
28
|
+
spans.push([start, end]);
|
|
29
|
+
found.push({ store, rowId, kind, prefix: value.slice(0, 4), length: value.length });
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return found;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface SecretScanDeps {
|
|
36
|
+
now?: () => Date;
|
|
37
|
+
stateRoot?: string;
|
|
38
|
+
logs?: Pick<LogStore, 'queryAll'>;
|
|
39
|
+
decisions?: Pick<DecisionLedger, 'list' | 'raise'>;
|
|
40
|
+
context?: (since: string) => CoordEvent[];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Only source coordinates and a four-character prefix leave the scanner. Missing stores are empty; unreadable stores throw. */
|
|
44
|
+
export function scanSecretExposures(options: { publish?: boolean } = {}, deps: SecretScanDeps = {}): SecretScanResult {
|
|
45
|
+
const root = deps.stateRoot ?? elanousStateRoot();
|
|
46
|
+
const nowMs = (deps.now ?? (() => new Date()))().getTime();
|
|
47
|
+
const sinceMs = nowMs - 86_400_000;
|
|
48
|
+
const since = new Date(sinceMs).toISOString();
|
|
49
|
+
const candidates: SecretCandidate[] = [];
|
|
50
|
+
const logPath = join(root, 'logs', 'logs.db');
|
|
51
|
+
const opened = !deps.logs && existsSync(logPath) ? LogStore.openReadOnly(logPath) : undefined;
|
|
52
|
+
const logs = deps.logs ?? opened;
|
|
53
|
+
try {
|
|
54
|
+
for (const row of logs?.queryAll({ sinceMs, untilMs: nowMs }) ?? []) {
|
|
55
|
+
if (!Number.isSafeInteger(row.id) || row.ts_ms < sinceMs || row.ts_ms > nowMs) continue;
|
|
56
|
+
const id = String(row.id);
|
|
57
|
+
candidates.push(...scanText('logs', id, JSON.stringify(row)));
|
|
58
|
+
}
|
|
59
|
+
} finally { opened?.close(); }
|
|
60
|
+
|
|
61
|
+
const ledger = deps.decisions ?? new DecisionLedger({ stateDir: root });
|
|
62
|
+
const entries = ledger.list({ status: 'all' });
|
|
63
|
+
for (const entry of entries) {
|
|
64
|
+
if (entry.raisedBy.agent === 'secret-exposure-scan' || !/^D-\d{8}-\d+$/.test(entry.id)) continue;
|
|
65
|
+
candidates.push(...scanText('decisions', entry.id, JSON.stringify(entry)));
|
|
66
|
+
}
|
|
67
|
+
for (const event of (deps.context ?? (s => listCoordEvents({ since: s })))(since)) {
|
|
68
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(event.id) || Date.parse(event.at) < sinceMs || Date.parse(event.at) > nowMs) continue;
|
|
69
|
+
candidates.push(...scanText('context', event.id, JSON.stringify(event)));
|
|
70
|
+
}
|
|
71
|
+
// One card per location/kind; do not put any untrusted source text into logs.
|
|
72
|
+
const unique = [...new Map(candidates.map(c => [`${c.store}:${c.rowId}:${c.kind}`, c])).values()];
|
|
73
|
+
let raised = 0;
|
|
74
|
+
if (options.publish) {
|
|
75
|
+
const existing = new Set(entries.flatMap(entry => entry.refs ?? []));
|
|
76
|
+
for (const candidate of unique) {
|
|
77
|
+
const ref = `secret-exposure:${candidate.store}:${candidate.rowId}:${candidate.kind}`;
|
|
78
|
+
if (existing.has(ref)) continue;
|
|
79
|
+
ledger.raise({ title: `비밀 노출 후보 — ${candidate.store} / ${candidate.rowId}`,
|
|
80
|
+
category: 'secret', scqa: { s: `${candidate.store} 행 ${candidate.rowId}에서 ${candidate.kind} 꼴이 발견됐다.`,
|
|
81
|
+
c: `값은 보관하지 않는다. 앞 4자 ${candidate.prefix} · 길이 ${candidate.length}.` },
|
|
82
|
+
options: [{ key: 'a', label: '원장 위치 확인 및 폐기', consequence: '사람이 원장에서 확인 후 폐기한다' },
|
|
83
|
+
{ key: 'b', label: '오탐으로 닫기', consequence: '사람이 오탐 여부를 확인한다' }],
|
|
84
|
+
recommendation: { skipped: true, reason: '사람 확인 필요' }, raisedBy: { agent: 'secret-exposure-scan' }, refs: [ref] });
|
|
85
|
+
existing.add(ref);
|
|
86
|
+
raised++;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return { mode: options.publish ? 'publish' : 'shadow', candidates: unique, raised };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (import.meta.main) {
|
|
93
|
+
// The graph runs without flags: shadow only. Publishing requires an explicit operator opt-in.
|
|
94
|
+
try {
|
|
95
|
+
const result = scanSecretExposures({ publish: process.argv.slice(2).includes('--publish') });
|
|
96
|
+
console.log(JSON.stringify(result));
|
|
97
|
+
} catch {
|
|
98
|
+
console.error('secret-exposure-scan: source unreadable');
|
|
99
|
+
process.exitCode = 1;
|
|
100
|
+
}
|
|
101
|
+
}
|