@askexenow/exe-os 0.9.302 → 0.9.310
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/deploy/compose/.env.example +8 -2
- package/deploy/compose/README.md +7 -3
- package/deploy/compose/add-routes.sh +107 -0
- package/deploy/compose/docker-compose.yml +91 -18
- package/deploy/compose/gateway-watchdog.sh +223 -0
- package/deploy/compose/generate-env.ts +16 -4
- package/deploy/compose/setup.sh +7 -8
- package/deploy/stack-manifests/v0.9.json +125 -4
- package/dist/{active-agent-ODI6GGVD.js → active-agent-ORX47ZKW.js} +4 -3
- package/dist/{active-agent-GGQAP4QE.js → active-agent-S5T2YMOR.js} +4 -3
- package/dist/{agentic-ontology-M7T72M6V.js → agentic-ontology-6KBAXLSJ.js} +1 -1
- package/dist/{backfill-metadata-GSZP2BEW.js → backfill-metadata-FV7GX6AS.js} +5 -4
- package/dist/{behaviors-JM6KFSXL.js → behaviors-47CPEKJ7.js} +6 -5
- package/dist/bin/agentic-ontology-backfill.js +6 -5
- package/dist/bin/agentic-reflection-backfill.js +7 -6
- package/dist/bin/agentic-semantic-label.js +6 -5
- package/dist/bin/backfill-conversations.js +8 -7
- package/dist/bin/backfill-responses.js +8 -7
- package/dist/bin/backfill-vectors.js +10 -9
- package/dist/bin/bulk-sync-postgres.js +14 -13
- package/dist/bin/cc-doctor.js +5 -4
- package/dist/bin/cleanup-stale-review-tasks.js +13 -12
- package/dist/bin/cli.js +20 -17
- package/dist/bin/deferred-daemon-restart.js +4 -4
- package/dist/bin/exe-agent-config.js +3 -2
- package/dist/bin/exe-agent.js +12 -11
- package/dist/bin/exe-assign.js +10 -9
- package/dist/bin/exe-boot.js +21 -20
- package/dist/bin/exe-call.js +5 -4
- package/dist/bin/exe-cloud.js +14 -13
- package/dist/bin/exe-config-dump.js +525 -0
- package/dist/bin/exe-dispatch.js +13 -12
- package/dist/bin/exe-doctor.js +2 -2
- package/dist/bin/exe-export-behaviors.js +8 -7
- package/dist/bin/exe-forget.js +7 -6
- package/dist/bin/exe-gateway.js +22 -11
- package/dist/bin/exe-healthcheck.js +7 -4
- package/dist/bin/exe-heartbeat.js +13 -12
- package/dist/bin/exe-kill.js +16 -15
- package/dist/bin/exe-launch-agent.js +41 -25
- package/dist/bin/exe-new-employee.js +9 -8
- package/dist/bin/exe-pending-messages.js +14 -13
- package/dist/bin/exe-pending-notifications.js +13 -12
- package/dist/bin/exe-pending-reviews.js +13 -12
- package/dist/bin/exe-preflight.js +108 -0
- package/dist/bin/exe-rename.js +5 -4
- package/dist/bin/exe-review.js +15 -14
- package/dist/bin/exe-search-quality.js +282 -0
- package/dist/bin/exe-search.js +6 -5
- package/dist/bin/exe-session-cleanup.js +18 -17
- package/dist/bin/exe-settings.js +15 -14
- package/dist/bin/exe-start-codex.js +13 -12
- package/dist/bin/exe-start-opencode.js +9 -8
- package/dist/bin/exe-status.js +14 -13
- package/dist/bin/exe-support.js +4 -4
- package/dist/bin/exe-team.js +4 -3
- package/dist/bin/exe-timers.js +237 -0
- package/dist/bin/git-sweep.js +14 -13
- package/dist/bin/graph-backfill.js +7 -6
- package/dist/bin/graph-export.js +6 -5
- package/dist/bin/import-history.js +10 -9
- package/dist/bin/install-launchd.js +5 -2
- package/dist/bin/install.js +16 -15
- package/dist/bin/intercom-check.js +4 -4
- package/dist/bin/mcp-sessions.js +2 -2
- package/dist/bin/orchestration-metrics.js +5 -4
- package/dist/bin/postgres-agentic-reflection-backfill.js +2 -2
- package/dist/bin/postgres-agentic-semantic-backfill.js +1 -1
- package/dist/bin/prune-orphan-chunks.js +43 -10
- package/dist/bin/scan-tasks.js +13 -12
- package/dist/bin/setup.js +1 -1
- package/dist/bin/shard-migrate.js +5 -4
- package/dist/bin/stack-update.js +30 -5
- package/dist/bin/vps-health-gate.js +1 -1
- package/dist/{capability-cards-BCTFKT4G.js → capability-cards-WQVY7WSS.js} +3 -2
- package/dist/{capacity-monitor-B4S3XEDO.js → capacity-monitor-BUKYPSWR.js} +14 -13
- package/dist/{catchup-brief-I35KHK62.js → catchup-brief-X5IVQAOH.js} +16 -15
- package/dist/{chunk-74TUYU5A.js → chunk-255DIG4B.js} +1 -1
- package/dist/{chunk-CXKR5Z5F.js → chunk-2EJ636HI.js} +4 -0
- package/dist/{chunk-ST3X3YMZ.js → chunk-2NGPZCWX.js} +4 -4
- package/dist/{chunk-LD2JKEJT.js → chunk-2NVEKSSP.js} +7 -7
- package/dist/{chunk-V6P7TPPA.js → chunk-2UVN2NBN.js} +148 -2
- package/dist/{chunk-OZRO37JW.js → chunk-3KH6GXXX.js} +2 -2
- package/dist/{chunk-IMJNYREY.js → chunk-43R2GILO.js} +1 -1
- package/dist/{chunk-6X2YNVLB.js → chunk-4BANMHJ5.js} +3 -3
- package/dist/{chunk-QNJPDIWY.js → chunk-4L4R6AQJ.js} +1 -1
- package/dist/{chunk-QWLPAEG5.js → chunk-4Q5VNQTV.js} +3 -3
- package/dist/{chunk-VLSYKMCJ.js → chunk-4RJJ5FJ2.js} +14 -2
- package/dist/{chunk-4AN6KMVG.js → chunk-5DPE7B7K.js} +1 -1
- package/dist/{chunk-H7ZNSQ2E.js → chunk-62UZWGFX.js} +2 -2
- package/dist/{chunk-ZQ2QHGWE.js → chunk-66JPZELX.js} +20 -24
- package/dist/{chunk-BUGMW7YF.js → chunk-6LNSZADA.js} +11 -11
- package/dist/{chunk-PBCRUNIK.js → chunk-6OD2RVIA.js} +492 -157
- package/dist/chunk-7ODTYODO.js +350 -0
- package/dist/{chunk-5YL4Y27D.js → chunk-7WDIGRSO.js} +1 -1
- package/dist/{chunk-WIL6LXNG.js → chunk-AC6DKMVS.js} +1 -1
- package/dist/{chunk-HZEAAKCR.js → chunk-B2J3BBJC.js} +3 -3
- package/dist/{chunk-LE7CDIOA.js → chunk-BUOMSIXH.js} +1 -1
- package/dist/{chunk-XKGNKH6V.js → chunk-DFUCFLZH.js} +1 -1
- package/dist/{chunk-U724Z3S3.js → chunk-DKHBMIVE.js} +100 -37
- package/dist/{chunk-MY36XDUK.js → chunk-DNFU56ZS.js} +1 -1
- package/dist/{chunk-GSWGOGP5.js → chunk-EOS7VPVT.js} +5 -5
- package/dist/{chunk-P33JVGSP.js → chunk-FM7XZDZI.js} +1 -1
- package/dist/chunk-G6EJKYK3.js +33 -0
- package/dist/{chunk-OMCNFTSI.js → chunk-GMZMBLHT.js} +1 -1
- package/dist/{chunk-4LZ54YGA.js → chunk-GSY6HL3T.js} +5 -5
- package/dist/{chunk-OVKNFTCL.js → chunk-HED6D5KR.js} +132 -21
- package/dist/{chunk-ALH6QLYF.js → chunk-HQODUV3G.js} +2 -2
- package/dist/{chunk-US64UR5O.js → chunk-IIHE3FNC.js} +3 -3
- package/dist/{chunk-H2ISTRWU.js → chunk-IRIPGGGL.js} +1 -1
- package/dist/{chunk-Z3EXECOX.js → chunk-ISILDO7P.js} +1 -1
- package/dist/{chunk-6ERZFDBS.js → chunk-IZPO6IOZ.js} +1 -1
- package/dist/{chunk-W65Z4SJS.js → chunk-J3QZPDNH.js} +1 -1
- package/dist/{chunk-3IJCP6ZH.js → chunk-JDYO76PI.js} +1 -1
- package/dist/{chunk-ORNKGGFX.js → chunk-JNARUXDC.js} +1 -1
- package/dist/{chunk-VME3UH4D.js → chunk-JV225JBX.js} +1 -1
- package/dist/{chunk-EI7PBUIA.js → chunk-JZW5AOWI.js} +1 -1
- package/dist/{chunk-TLOILHSL.js → chunk-K7KBAJQL.js} +36 -18
- package/dist/{chunk-RHMWSUJB.js → chunk-KAVUTP5G.js} +2 -2
- package/dist/{chunk-7FNELLMX.js → chunk-KPVVTHPK.js} +8 -8
- package/dist/{chunk-35U72CD2.js → chunk-KSMITUGY.js} +3 -3
- package/dist/{chunk-D6UICP3C.js → chunk-LMX7UKGX.js} +81 -3
- package/dist/{chunk-7ZWC5HVG.js → chunk-MTIJ2NXY.js} +1 -1
- package/dist/{chunk-KVHPBQV4.js → chunk-NCC3F6GM.js} +1 -1
- package/dist/{chunk-AYMSIV7X.js → chunk-NIZQ3CIS.js} +1 -1
- package/dist/{chunk-LTI2DLHY.js → chunk-OBJXZII2.js} +2 -2
- package/dist/{chunk-WHOT5NZ7.js → chunk-OR3TAZL3.js} +14 -2
- package/dist/{chunk-RQUHAXMM.js → chunk-PHFPIUWU.js} +2 -0
- package/dist/{chunk-N76VTMMF.js → chunk-PMEOSD6S.js} +2 -2
- package/dist/{chunk-TCJEFW5V.js → chunk-PPXQE7AS.js} +4 -4
- package/dist/{chunk-KHZQVLNZ.js → chunk-QE7UVQDP.js} +46 -0
- package/dist/{chunk-SXJACGQ5.js → chunk-QEGXNJGR.js} +4 -4
- package/dist/{chunk-W3E4VEEJ.js → chunk-RPBTYQKJ.js} +1 -1
- package/dist/chunk-RQ7OVXUZ.js +81 -0
- package/dist/{chunk-7L2EV3XX.js → chunk-RVXWGPD3.js} +39 -6
- package/dist/{chunk-YNALY3SX.js → chunk-RZJ6DXVE.js} +1 -1
- package/dist/{chunk-P3SIFDES.js → chunk-SCYWCWKY.js} +16 -4
- package/dist/{chunk-7I43GVER.js → chunk-SOT23KWW.js} +3 -3
- package/dist/{chunk-T62GAN7I.js → chunk-SPOXL7HF.js} +7 -7
- package/dist/{chunk-3YJMI422.js → chunk-TN2YCFQE.js} +4 -4
- package/dist/{chunk-YMSWCERY.js → chunk-U63BZI2B.js} +3 -3
- package/dist/{chunk-KA5RK5HZ.js → chunk-V42BSVX6.js} +2 -2
- package/dist/{chunk-PJTRAFL4.js → chunk-V4BW5HFQ.js} +3 -3
- package/dist/{chunk-4FG6IX7U.js → chunk-VFSYVQX4.js} +1 -1
- package/dist/chunk-VQRTO7IS.js +290 -0
- package/dist/{chunk-OR7J7TIA.js → chunk-VT6P7FV4.js} +1 -1
- package/dist/{chunk-BEIYF4NS.js → chunk-W2J2RF5N.js} +3 -3
- package/dist/{chunk-Y5ASPRP7.js → chunk-WBJXJ2R4.js} +17 -13
- package/dist/chunk-WCGNCH3Q.js +72 -0
- package/dist/{chunk-2LJDLQNO.js → chunk-WJ2PZGRV.js} +1 -1
- package/dist/{chunk-AAPPSMPH.js → chunk-WZBUKC5N.js} +1 -1
- package/dist/{chunk-RO7EXVSJ.js → chunk-X4AFBL4N.js} +1 -1
- package/dist/{chunk-BR74NYEF.js → chunk-XV5HRIXG.js} +5 -5
- package/dist/chunk-XXJRZ75E.js +43 -0
- package/dist/{chunk-VDHUHXFF.js → chunk-XYG722JW.js} +2 -2
- package/dist/{chunk-MM7SWUQ4.js → chunk-YB7U4WTY.js} +4 -4
- package/dist/{chunk-RMRZXPUU.js → chunk-Z6H6Y4BS.js} +1 -1
- package/dist/{token-budget-UMHRRDBT.js → chunk-ZOVHXTGC.js} +2 -9
- package/dist/{chunk-HNLBHP6P.js → chunk-ZZXADKAP.js} +3 -3
- package/dist/{co-activation-OKAHEMPE.js → co-activation-WN25AJOC.js} +3 -2
- package/dist/{co-occurrence-IHSPB53O.js → co-occurrence-YP2Q3OEA.js} +5 -4
- package/dist/{code-context-index-KGY4LKCA.js → code-context-index-ZWONKBKX.js} +4 -4
- package/dist/{conversation-entity-extractor-KXJSHRGJ.js → conversation-entity-extractor-KYOBXNID.js} +2 -2
- package/dist/{crdt-sync-K2G3C6XY.js → crdt-sync-ZFIFL2QM.js} +1 -1
- package/dist/{crm-webhook-ERZSQ3CW.js → crm-webhook-DGPHULOO.js} +2 -2
- package/dist/{cto-delegation-gate-ZCJ53LBQ.js → cto-delegation-gate-OF2A3TSS.js} +12 -11
- package/dist/daemon-auth-N6P2MZAV.js +17 -0
- package/dist/{daemon-orchestration-V6HALVDS.js → daemon-orchestration-G4RRTPP7.js} +16 -15
- package/dist/daemon-ready-signal-3L2MJT7C.js +13 -0
- package/dist/{db-backup-VZHSFUVA.js → db-backup-EORG4LBO.js} +1 -1
- package/dist/{doc-graph-extractor-LQ2IFND2.js → doc-graph-extractor-APZS3DFD.js} +5 -4
- package/dist/{dreaming-QAKUASYV.js → dreaming-Q4E3WBGY.js} +13 -12
- package/dist/{exe-drift-H3UWNFFY.js → exe-drift-CI5R6GUT.js} +4 -3
- package/dist/{exe-export-MN5D4JMT.js → exe-export-HQIQASIT.js} +7 -6
- package/dist/{exe-import-IFFMEVD7.js → exe-import-ULZK56WC.js} +7 -6
- package/dist/{exe-key-RSZC72QK.js → exe-key-ILNSBGRN.js} +5 -4
- package/dist/{exe-snapshot-I622MPLS.js → exe-snapshot-RUZTWZOF.js} +16 -15
- package/dist/{factory-CTFAGZUA.js → factory-YHQZ6RZZ.js} +14 -6
- package/dist/{fast-db-init-QEFVT7Q3.js → fast-db-init-KNJRL7T3.js} +1 -1
- package/dist/{founder-context-3SOKEW76.js → founder-context-QCTMEK66.js} +2 -2
- package/dist/gateway/index.js +12 -11
- package/dist/{git-staleness-E7RPQA5U.js → git-staleness-KIA5YI4H.js} +3 -2
- package/dist/{git-task-sweep-D3A2C5BW.js → git-task-sweep-WH6QE7HL.js} +13 -12
- package/dist/{global-procedures-POG4V6IK.js → global-procedures-SYZZCGDS.js} +4 -3
- package/dist/{graph-auto-extract-E7KALNWA.js → graph-auto-extract-J2TIWZEE.js} +5 -4
- package/dist/{graph-rag-3RZN7JR3.js → graph-rag-HITZZ63L.js} +2 -2
- package/dist/hooks/bug-report-worker.js +15 -14
- package/dist/hooks/codex-stop-task-finalizer.js +15 -14
- package/dist/hooks/commit-complete.js +15 -14
- package/dist/hooks/error-recall.js +8 -7
- package/dist/hooks/exe-heartbeat-hook.js +4 -3
- package/dist/hooks/ingest-worker.js +4 -4
- package/dist/hooks/ingest.js +8 -7
- package/dist/hooks/instructions-loaded.js +5 -4
- package/dist/hooks/manifest.json +20 -20
- package/dist/hooks/notification.js +5 -4
- package/dist/hooks/post-compact.js +14 -13
- package/dist/hooks/post-tool-combined.js +7 -7
- package/dist/hooks/pre-compact.js +19 -18
- package/dist/hooks/pre-tool-use.js +18 -17
- package/dist/hooks/prompt-submit.js +28 -27
- package/dist/hooks/session-end.js +24 -23
- package/dist/hooks/session-start.js +46 -22
- package/dist/hooks/stop.js +22 -21
- package/dist/hooks/subagent-stop.js +14 -13
- package/dist/hooks/summary-worker.js +22 -21
- package/dist/index.js +25 -23
- package/dist/{installer-IXUX73JN.js → installer-3NB3IBHF.js} +10 -9
- package/dist/{installer-RCUPPZ7Y.js → installer-IIJFCTMH.js} +10 -9
- package/dist/{installer-HDD6OH5Z.js → installer-PSDYMGN7.js} +7 -6
- package/dist/lib/cloud-sync.js +14 -13
- package/dist/lib/consolidation.js +8 -7
- package/dist/lib/database.js +3 -2
- package/dist/lib/db-daemon-client.js +4 -4
- package/dist/lib/db.js +3 -2
- package/dist/lib/device-registry.js +3 -1
- package/dist/lib/embedder.js +4 -4
- package/dist/lib/employee-templates.js +5 -4
- package/dist/lib/employees.js +3 -2
- package/dist/lib/exe-daemon-client.js +3 -3
- package/dist/lib/exe-daemon.js +256 -406
- package/dist/lib/hybrid-search.js +6 -5
- package/dist/lib/identity.js +3 -2
- package/dist/lib/license.js +2 -2
- package/dist/lib/messaging.js +13 -12
- package/dist/lib/reminders.js +4 -3
- package/dist/lib/schedules.js +6 -5
- package/dist/lib/session-registry.js +5 -4
- package/dist/lib/skill-learning.js +7 -6
- package/dist/lib/store.js +7 -4
- package/dist/lib/task-router.js +4 -3
- package/dist/lib/tasks.js +14 -13
- package/dist/lib/tmux-routing.js +16 -11
- package/dist/lib/token-spend.js +4 -3
- package/dist/{license-gate-V2UCK4KJ.js → license-gate-7SDHCJUO.js} +3 -3
- package/dist/mcp/register-tools.js +74 -73
- package/dist/mcp/server.js +97 -79
- package/dist/mcp/tools/complete-reminder.js +5 -4
- package/dist/mcp/tools/create-reminder.js +5 -4
- package/dist/mcp/tools/create-task.js +16 -15
- package/dist/mcp/tools/deactivate-behavior.js +8 -7
- package/dist/mcp/tools/list-reminders.js +5 -4
- package/dist/mcp/tools/list-tasks.js +16 -15
- package/dist/mcp/tools/send-message.js +15 -14
- package/dist/mcp/tools/update-task.js +15 -14
- package/dist/{mcp-http-config-7LFFPNGV.js → mcp-http-config-QSOVFXHW.js} +4 -3
- package/dist/mcp-tool-surface-CSPFXL3F.js +15 -0
- package/dist/{memory-cards-BGI6MJRN.js → memory-cards-II67WFL2.js} +3 -2
- package/dist/{memory-graph-extractor-DTRRNWHZ.js → memory-graph-extractor-PM4Q2KJG.js} +6 -5
- package/dist/{memory-poisoning-defense-MOCWJK6S.js → memory-poisoning-defense-ECXSX7HK.js} +3 -2
- package/dist/{memory-queue-client-DD4E5SIZ.js → memory-queue-client-HLURQEV6.js} +4 -4
- package/dist/{memory-reflection-UKKTGZYR.js → memory-reflection-PXF6YFDU.js} +3 -2
- package/dist/{notifications-AMJ2K3MM.js → notifications-UP7WQGG4.js} +12 -11
- package/dist/{orchestration-events-MB4QVHTD.js → orchestration-events-PZLLPCKY.js} +4 -3
- package/dist/orchestration-health-read-EJWVUARG.js +191 -0
- package/dist/{orchestrator-M7DJH6TN.js → orchestrator-PUT45J4O.js} +14 -13
- package/dist/{pipeline-router-4LT5YG5P.js → pipeline-router-5CRWKESM.js} +4 -3
- package/dist/{plan-limits-ENS7OJWP.js → plan-limits-SH2NDFNX.js} +6 -5
- package/dist/{project-boot-6W5JPGSB.js → project-boot-2TCPDZ4S.js} +25 -1
- package/dist/{projection-worker-WWXZK7QD.js → projection-worker-HWY37MNC.js} +2 -2
- package/dist/{prospective-memory-VNGLPMS6.js → prospective-memory-FQSVMCGG.js} +3 -2
- package/dist/{reranker-6XBS55FA.js → reranker-AALRMGMM.js} +1 -1
- package/dist/{retrieval-health-ATX5TIQ2.js → retrieval-health-PNWJSJNG.js} +1 -1
- package/dist/{review-polling-E4ALHTQE.js → review-polling-65JZUE3T.js} +13 -12
- package/dist/runtime/index.js +24 -22
- package/dist/{session-events-3J6APMKN.js → session-events-EJ3OSAA6.js} +13 -12
- package/dist/session-kill-telemetry-3HGM7SNQ.js +63 -0
- package/dist/{session-scope-E54VIYTJ.js → session-scope-JGDYAMJO.js} +12 -11
- package/dist/{setup-wizard-XV736U5D.js → setup-wizard-44LMFM2C.js} +1 -1
- package/dist/shared-rate-limit-store-QZPQEVE2.js +80 -0
- package/dist/{skill-refinement-FTURVWFN.js → skill-refinement-DUOO6B7V.js} +3 -2
- package/dist/{stack-release-JZVTSBJJ.js → stack-release-XYMKU7NT.js} +53 -4
- package/dist/{stack-update-BIK2QHOE.js → stack-update-XHQJRJEP.js} +5 -3
- package/dist/{steward-gate-YGU4VXPP.js → steward-gate-6NWRDLX2.js} +4 -3
- package/dist/{task-enforcement-GY3VTY6L.js → task-enforcement-UOULTGFB.js} +12 -11
- package/dist/{task-scope-PYOK7OQ6.js → task-scope-P5STKYAC.js} +12 -11
- package/dist/{tasks-crud-HLJEJ2UU.js → tasks-crud-24HMTULR.js} +12 -11
- package/dist/{tasks-notify-NGS3DWUJ.js → tasks-notify-LAIU7UUI.js} +13 -12
- package/dist/{tasks-review-BP5V2X34.js → tasks-review-AFJAU2DY.js} +12 -11
- package/dist/{telemetry-upload-AXYPFAPS.js → telemetry-upload-DWFGC2XJ.js} +8 -7
- package/dist/token-budget-4RFZKWGH.js +16 -0
- package/dist/{tool-capability-index-RHPVMMDD.js → tool-capability-index-ZEXBWKUZ.js} +1 -1
- package/dist/{tool-telemetry-BLSVABFJ.js → tool-telemetry-BS5JXM5M.js} +1 -1
- package/dist/tui/App.js +21 -20
- package/dist/{tui-data-EYPOFYLS.js → tui-data-UNO5B7PU.js} +12 -11
- package/dist/{worker-gate-SMFOBMMX.js → worker-gate-C6CTTYMD.js} +1 -1
- package/dist/{workflow-engine-TDJZ3TXB.js → workflow-engine-4W2KYCDV.js} +2 -2
- package/dist/{worktree-XISIYRM4.js → worktree-2CXY6NEB.js} +5 -4
- package/dist/{worktree-sweep-AS6GAR6X.js → worktree-sweep-K3CJFSUL.js} +6 -5
- package/package.json +9 -4
- package/release-notes.json +17 -88
- package/stack.release.json +51 -4
- package/dist/chunk-PHOQAVTK.js +0 -42
- package/dist/daemon-auth-BK7ON5ZH.js +0 -13
- package/dist/session-kill-telemetry-KOAHLRBT.js +0 -31
- package/dist/{chunk-XDE5IGBX.js → chunk-2NS3QRMJ.js} +0 -0
- package/dist/{chunk-JTMKRUX6.js → chunk-A2YZG4AD.js} +0 -0
- package/dist/{chunk-UZATXREL.js → chunk-AE74NOE6.js} +0 -0
- package/dist/{chunk-LUGWK6QB.js → chunk-FF5DA4XP.js} +3 -3
- /package/dist/{chunk-BGOMFGSW.js → chunk-GWPICNSO.js} +0 -0
- /package/dist/{chunk-NLHUA3YB.js → chunk-I3DPCSDG.js} +0 -0
- /package/dist/{chunk-YM3367VC.js → chunk-J3SR5MGG.js} +0 -0
- /package/dist/{chunk-FKFVNV7X.js → chunk-K4BKJJ4F.js} +0 -0
- /package/dist/{chunk-PTDBHQX7.js → chunk-MQCTK6EF.js} +0 -0
- /package/dist/{chunk-GBV5PV5T.js → chunk-QNXM2HXW.js} +0 -0
- /package/dist/{chunk-GZSG2ZOX.js → chunk-YUBTHJVV.js} +0 -0
- /package/dist/{core-memory-4OM7LR77.js → core-memory-OC5ITKKV.js} +0 -0
- /package/dist/{entity-boost-SFHIRXT5.js → entity-boost-NF3LE6OJ.js} +0 -0
- /package/dist/{message-queue-client-QTC3VHJT.js → message-queue-client-4VDU6BZY.js} +0 -0
- /package/dist/{oauth-server-BPN55EJM.js → oauth-server-EWUL7DMD.js} +0 -0
- /package/dist/{wiki-acl-45SIGG4O.js → wiki-acl-CONNF6ES.js} +0 -0
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
// src/lib/preflight-checks.ts
|
|
2
|
+
import { readFileSync, existsSync } from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
var REQUIRED_INFRA = {
|
|
5
|
+
postgres: ["exe-db", "postgres", "db", "exe-db-jkt"],
|
|
6
|
+
redis: ["redis"],
|
|
7
|
+
clickhouse: ["clickhouse"],
|
|
8
|
+
"exe-sso-edge": ["exe-sso-edge", "sso-edge"],
|
|
9
|
+
cloudflared: ["cloudflared"]
|
|
10
|
+
};
|
|
11
|
+
var FORBIDDEN_HOST_SUBSTRINGS = ["askexe.com", "askexenow.com"];
|
|
12
|
+
var CLIENT_FACING_HOST_FIELDS = ["healthHost", "healthUrl"];
|
|
13
|
+
var BYOK_ENFORCEMENT_VARS = ["EXE_ALLOW_DIRECT_PROVIDER_KEYS", "EXE_BILLING_MODE"];
|
|
14
|
+
var BYOK_LEGACY_VARS = ["BYOK_ENABLED"];
|
|
15
|
+
function resolveDefaultManifest() {
|
|
16
|
+
let dir = path.dirname(new URL(import.meta.url).pathname);
|
|
17
|
+
for (let i = 0; i < 6; i++) {
|
|
18
|
+
const candidate = path.join(dir, "deploy", "stack-manifests", "v0.9.json");
|
|
19
|
+
if (existsSync(candidate)) return candidate;
|
|
20
|
+
const parent = path.dirname(dir);
|
|
21
|
+
if (parent === dir) break;
|
|
22
|
+
dir = parent;
|
|
23
|
+
}
|
|
24
|
+
return path.join(process.cwd(), "deploy", "stack-manifests", "v0.9.json");
|
|
25
|
+
}
|
|
26
|
+
function parseImageRef(image) {
|
|
27
|
+
const hasDigest = /@sha256:[0-9a-f]{64}/i.test(image);
|
|
28
|
+
const withoutDigest = image.replace(/@sha256:[0-9a-f]{64}/i, "");
|
|
29
|
+
const lastSlash = withoutDigest.lastIndexOf("/");
|
|
30
|
+
const namePart = lastSlash >= 0 ? withoutDigest.slice(lastSlash + 1) : withoutDigest;
|
|
31
|
+
const colonIdx = namePart.indexOf(":");
|
|
32
|
+
const hasTag = colonIdx >= 0;
|
|
33
|
+
const tag = hasTag ? namePart.slice(colonIdx + 1) : "";
|
|
34
|
+
return { hasDigest, hasTag, tag };
|
|
35
|
+
}
|
|
36
|
+
function loadEnvLikeFile(filePath) {
|
|
37
|
+
const vars = /* @__PURE__ */ new Set();
|
|
38
|
+
if (!filePath || !existsSync(filePath)) return { vars, exists: false };
|
|
39
|
+
const raw = readFileSync(filePath, "utf8");
|
|
40
|
+
for (const line of raw.split("\n")) {
|
|
41
|
+
const m = line.match(/^\s*#?\s*([A-Z][A-Z0-9_]+)\s*=/);
|
|
42
|
+
if (m) vars.add(m[1]);
|
|
43
|
+
}
|
|
44
|
+
return { vars, exists: true };
|
|
45
|
+
}
|
|
46
|
+
function readComposeText(composePath) {
|
|
47
|
+
if (!composePath || !existsSync(composePath)) return { text: "", exists: false };
|
|
48
|
+
return { text: readFileSync(composePath, "utf8"), exists: true };
|
|
49
|
+
}
|
|
50
|
+
function resolveStack(manifest, requested) {
|
|
51
|
+
if (manifest.services && !manifest.stacks) {
|
|
52
|
+
return { version: manifest.version ?? "(single)", services: manifest.services };
|
|
53
|
+
}
|
|
54
|
+
if (!manifest.stacks) return { version: "(none)", services: {} };
|
|
55
|
+
const version = requested ?? manifest.latest ?? Object.keys(manifest.stacks).sort().pop() ?? "";
|
|
56
|
+
const entry = manifest.stacks[version];
|
|
57
|
+
return { version, services: entry?.services ?? {} };
|
|
58
|
+
}
|
|
59
|
+
function checkDigestPinning(services) {
|
|
60
|
+
const bug = "ee16e7ef (digest-pinning)";
|
|
61
|
+
const offenders = [];
|
|
62
|
+
for (const [name, svc] of Object.entries(services)) {
|
|
63
|
+
if (!svc.image) continue;
|
|
64
|
+
const { hasDigest } = parseImageRef(svc.image);
|
|
65
|
+
if (!hasDigest) offenders.push(`${name}=${svc.image}`);
|
|
66
|
+
}
|
|
67
|
+
if (offenders.length > 0) {
|
|
68
|
+
return {
|
|
69
|
+
check: "digest-pinning",
|
|
70
|
+
status: "fail",
|
|
71
|
+
bug,
|
|
72
|
+
offenders,
|
|
73
|
+
message: `${offenders.length} image(s) tag-pinned WITHOUT @sha256 digest (release claims digest-pinning): ${offenders.join(", ")}`
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
return { check: "digest-pinning", status: "pass", bug, message: "all images @sha256 digest-pinned" };
|
|
77
|
+
}
|
|
78
|
+
function checkNoLatest(services) {
|
|
79
|
+
const bug = "monitor :latest / no-:latest";
|
|
80
|
+
const offenders = [];
|
|
81
|
+
for (const [name, svc] of Object.entries(services)) {
|
|
82
|
+
if (!svc.image) continue;
|
|
83
|
+
const { hasDigest, hasTag, tag } = parseImageRef(svc.image);
|
|
84
|
+
if (tag === "latest") offenders.push(`${name}=${svc.image}`);
|
|
85
|
+
else if (!hasTag && !hasDigest) offenders.push(`${name}=${svc.image} (untagged \u2192 implicit :latest)`);
|
|
86
|
+
}
|
|
87
|
+
if (offenders.length > 0) {
|
|
88
|
+
return {
|
|
89
|
+
check: "no-latest",
|
|
90
|
+
status: "fail",
|
|
91
|
+
bug,
|
|
92
|
+
offenders,
|
|
93
|
+
message: `${offenders.length} image(s) use :latest / are untagged: ${offenders.join(", ")}`
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
return { check: "no-latest", status: "pass", bug, message: "no :latest / untagged images" };
|
|
97
|
+
}
|
|
98
|
+
function checkDependencyClosure(services) {
|
|
99
|
+
const bug = "4948fc48 (dependency-closure)";
|
|
100
|
+
const present = /* @__PURE__ */ new Set();
|
|
101
|
+
for (const [name, svc] of Object.entries(services)) {
|
|
102
|
+
present.add(name);
|
|
103
|
+
if (svc.composeService) present.add(svc.composeService);
|
|
104
|
+
}
|
|
105
|
+
const missing = [];
|
|
106
|
+
for (const [canonical, aliases] of Object.entries(REQUIRED_INFRA)) {
|
|
107
|
+
if (!aliases.some((a) => present.has(a))) missing.push(canonical);
|
|
108
|
+
}
|
|
109
|
+
if (missing.length > 0) {
|
|
110
|
+
return {
|
|
111
|
+
check: "dependency-closure",
|
|
112
|
+
status: "fail",
|
|
113
|
+
bug,
|
|
114
|
+
offenders: missing,
|
|
115
|
+
message: `manifest missing required infra dependency closure \u2014 selected/fresh installs would skip: ${missing.join(", ")}`
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
return { check: "dependency-closure", status: "pass", bug, message: `all required infra present (${Object.keys(REQUIRED_INFRA).join(", ")})` };
|
|
119
|
+
}
|
|
120
|
+
function checkNoHardcodedHost(services) {
|
|
121
|
+
const bug = "ERP white-label-leak (no-hardcoded-askexe-host)";
|
|
122
|
+
const offenders = [];
|
|
123
|
+
for (const [name, svc] of Object.entries(services)) {
|
|
124
|
+
for (const field of CLIENT_FACING_HOST_FIELDS) {
|
|
125
|
+
const val = svc[field];
|
|
126
|
+
if (typeof val !== "string") continue;
|
|
127
|
+
const hit = FORBIDDEN_HOST_SUBSTRINGS.find((h) => val.includes(h));
|
|
128
|
+
if (hit) offenders.push(`${name}.${field}=${val}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (offenders.length > 0) {
|
|
132
|
+
return {
|
|
133
|
+
check: "no-hardcoded-askexe-host",
|
|
134
|
+
status: "fail",
|
|
135
|
+
bug,
|
|
136
|
+
offenders,
|
|
137
|
+
message: `client-facing field(s) hardcode an AskExe host (white-label leak): ${offenders.join(", ")}`
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
return { check: "no-hardcoded-askexe-host", status: "pass", bug, message: "no hardcoded AskExe hosts in client-facing fields" };
|
|
141
|
+
}
|
|
142
|
+
function checkEnvContract(env, compose) {
|
|
143
|
+
const bug = "BYOK env-contract-mismatch";
|
|
144
|
+
if (!env.exists && !compose.exists) {
|
|
145
|
+
return {
|
|
146
|
+
check: "env-contract",
|
|
147
|
+
status: "warn",
|
|
148
|
+
bug,
|
|
149
|
+
message: "env example + compose not provided \u2014 BYOK enforcement contract not verified (pass --env / --compose to enforce)"
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
const declared = new Set(env.vars);
|
|
153
|
+
if (compose.exists) {
|
|
154
|
+
for (const m of compose.text.matchAll(/\$\{?([A-Z][A-Z0-9_]+)/g)) declared.add(m[1]);
|
|
155
|
+
}
|
|
156
|
+
const missingEnforcement = BYOK_ENFORCEMENT_VARS.filter((v) => !declared.has(v));
|
|
157
|
+
const exposedLegacy = BYOK_LEGACY_VARS.filter((v) => declared.has(v));
|
|
158
|
+
if (missingEnforcement.length > 0 && exposedLegacy.length > 0) {
|
|
159
|
+
return {
|
|
160
|
+
check: "env-contract",
|
|
161
|
+
status: "fail",
|
|
162
|
+
bug,
|
|
163
|
+
offenders: [...exposedLegacy.map((v) => `exposes ${v}`), ...missingEnforcement.map((v) => `missing ${v}`)],
|
|
164
|
+
message: `BYOK env-contract mismatch \u2014 customer surface exposes ${exposedLegacy.join("/")} but billing enforcement reads ${BYOK_ENFORCEMENT_VARS.join("+")} (absent). BYOK can bypass billing.`
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
if (missingEnforcement.length > 0) {
|
|
168
|
+
return {
|
|
169
|
+
check: "env-contract",
|
|
170
|
+
status: "warn",
|
|
171
|
+
bug,
|
|
172
|
+
offenders: missingEnforcement.map((v) => `missing ${v}`),
|
|
173
|
+
message: `billing enforcement vars not declared: ${missingEnforcement.join(", ")}`
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
return { check: "env-contract", status: "pass", bug, message: `BYOK enforcement vars present (${BYOK_ENFORCEMENT_VARS.join(", ")})` };
|
|
177
|
+
}
|
|
178
|
+
function checkNoHostNet(composeTexts) {
|
|
179
|
+
const bug = "monitor host-networking (no-host-net)";
|
|
180
|
+
const available = composeTexts.filter((c) => c.exists);
|
|
181
|
+
if (available.length === 0) {
|
|
182
|
+
return {
|
|
183
|
+
check: "no-host-net",
|
|
184
|
+
status: "warn",
|
|
185
|
+
bug,
|
|
186
|
+
message: "no compose file provided \u2014 host-networking not verified (pass --compose / --monitor-compose to enforce)"
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
const HOST_NET = /^\s*network_mode\s*:\s*["']?host["']?\s*$/;
|
|
190
|
+
const HOST_NET_FLAG = /--network(?:=|\s+)["']?host["']?/;
|
|
191
|
+
const offenders = [];
|
|
192
|
+
for (const compose of available) {
|
|
193
|
+
const lines = compose.text.split("\n");
|
|
194
|
+
let currentService = null;
|
|
195
|
+
let serviceIndent = -1;
|
|
196
|
+
for (const rawLine of lines) {
|
|
197
|
+
const line = rawLine.replace(/\r$/, "");
|
|
198
|
+
const trimmed = line.trim();
|
|
199
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
200
|
+
const indent = line.match(/^(\s*)/)?.[1]?.length ?? 0;
|
|
201
|
+
const headerMatch = line.match(/^(\s+)([A-Za-z0-9._-]+):\s*$/);
|
|
202
|
+
if (headerMatch && (currentService === null || indent <= serviceIndent)) {
|
|
203
|
+
currentService = headerMatch[2];
|
|
204
|
+
serviceIndent = headerMatch[1].length;
|
|
205
|
+
}
|
|
206
|
+
if (HOST_NET.test(line) || HOST_NET_FLAG.test(line)) {
|
|
207
|
+
const where = currentService ? `${compose.label}:${currentService}` : compose.label;
|
|
208
|
+
offenders.push(`${where} (${trimmed})`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
if (offenders.length > 0) {
|
|
213
|
+
return {
|
|
214
|
+
check: "no-host-net",
|
|
215
|
+
status: "fail",
|
|
216
|
+
bug,
|
|
217
|
+
offenders,
|
|
218
|
+
message: `compose service(s) use host networking (network_mode: host) \u2014 bypasses Docker isolation + SSO edge: ${offenders.join(", ")}`
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
return { check: "no-host-net", status: "pass", bug, message: "no host networking (network_mode: host) in compose" };
|
|
222
|
+
}
|
|
223
|
+
function runChecks(services, env, compose, monitorCompose, customerComposeLabel) {
|
|
224
|
+
return [
|
|
225
|
+
checkDigestPinning(services),
|
|
226
|
+
checkNoLatest(services),
|
|
227
|
+
checkDependencyClosure(services),
|
|
228
|
+
checkNoHardcodedHost(services),
|
|
229
|
+
checkEnvContract(env, compose),
|
|
230
|
+
checkNoHostNet([
|
|
231
|
+
{ label: customerComposeLabel, text: compose.text, exists: compose.exists },
|
|
232
|
+
monitorCompose
|
|
233
|
+
])
|
|
234
|
+
];
|
|
235
|
+
}
|
|
236
|
+
function tally(findings) {
|
|
237
|
+
const passed = findings.filter((f) => f.status === "pass").length;
|
|
238
|
+
const warned = findings.filter((f) => f.status === "warn").length;
|
|
239
|
+
const failed = findings.filter((f) => f.status === "fail").length;
|
|
240
|
+
const verdict = failed > 0 ? "RED" : warned > 0 ? "YELLOW" : "GREEN";
|
|
241
|
+
return { verdict, passed, warned, failed };
|
|
242
|
+
}
|
|
243
|
+
function runPreflight(options) {
|
|
244
|
+
const manifestPath = options?.manifestPath ?? resolveDefaultManifest();
|
|
245
|
+
if (!existsSync(manifestPath)) {
|
|
246
|
+
return {
|
|
247
|
+
verdict: "RED",
|
|
248
|
+
manifestPath,
|
|
249
|
+
stackVersion: "(missing)",
|
|
250
|
+
findings: [{ check: "manifest-load", status: "fail", bug: "preflight", message: `manifest not found: ${manifestPath}` }],
|
|
251
|
+
passed: 0,
|
|
252
|
+
warned: 0,
|
|
253
|
+
failed: 1
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
let manifest;
|
|
257
|
+
try {
|
|
258
|
+
manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
259
|
+
} catch (err) {
|
|
260
|
+
return {
|
|
261
|
+
verdict: "RED",
|
|
262
|
+
manifestPath,
|
|
263
|
+
stackVersion: "(parse-error)",
|
|
264
|
+
findings: [{ check: "manifest-load", status: "fail", bug: "preflight", message: `manifest parse error: ${err instanceof Error ? err.message : String(err)}` }],
|
|
265
|
+
passed: 0,
|
|
266
|
+
warned: 0,
|
|
267
|
+
failed: 1
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
const { version, services } = resolveStack(manifest, options?.stackVersion);
|
|
271
|
+
const repoRoot = path.resolve(path.dirname(manifestPath), "..", "..");
|
|
272
|
+
const envExamplePath = options?.envExamplePath ?? path.join(repoRoot, "deploy", "compose", ".env.example");
|
|
273
|
+
const composePath = options?.composePath ?? path.join(repoRoot, "deploy", "compose", "docker-compose.yml");
|
|
274
|
+
const monitorComposePath = options?.monitorComposePath ?? path.join(repoRoot, "deploy", "monitor", "docker-compose.hub.yml");
|
|
275
|
+
const env = loadEnvLikeFile(envExamplePath);
|
|
276
|
+
const compose = readComposeText(composePath);
|
|
277
|
+
const monitorComposeRead = readComposeText(monitorComposePath);
|
|
278
|
+
const monitorCompose = { label: "monitor-compose", text: monitorComposeRead.text, exists: monitorComposeRead.exists };
|
|
279
|
+
const findings = runChecks(services, env, compose, monitorCompose, "customer-compose");
|
|
280
|
+
const { verdict, passed, warned, failed } = tally(findings);
|
|
281
|
+
return { verdict, manifestPath, stackVersion: version, findings, passed, warned, failed };
|
|
282
|
+
}
|
|
283
|
+
function runPreflightOnManifest(input) {
|
|
284
|
+
const label = input.label ?? "(in-memory manifest)";
|
|
285
|
+
const { version, services } = resolveStack(input.manifest, input.stackVersion);
|
|
286
|
+
const env = loadEnvLikeFile(input.envExamplePath);
|
|
287
|
+
const compose = readComposeText(input.composePath);
|
|
288
|
+
const monitorComposeRead = readComposeText(input.monitorComposePath);
|
|
289
|
+
const monitorCompose = { label: "monitor-compose", text: monitorComposeRead.text, exists: monitorComposeRead.exists };
|
|
290
|
+
const findings = runChecks(services, env, compose, monitorCompose, "customer-compose");
|
|
291
|
+
const { verdict, passed, warned, failed } = tally(findings);
|
|
292
|
+
return { verdict, manifestPath: label, stackVersion: version, findings, passed, warned, failed };
|
|
293
|
+
}
|
|
294
|
+
var ICONS = { pass: "\u{1F7E2}", warn: "\u{1F7E0}", fail: "\u{1F534}" };
|
|
295
|
+
function formatPreflightFindings(report, all = false) {
|
|
296
|
+
const lines = [];
|
|
297
|
+
for (const f of report.findings) {
|
|
298
|
+
if (!all && f.status === "pass") continue;
|
|
299
|
+
lines.push(` ${ICONS[f.status]} ${f.check} [guards ${f.bug}]: ${f.message}`);
|
|
300
|
+
}
|
|
301
|
+
return lines.join("\n");
|
|
302
|
+
}
|
|
303
|
+
function isPreflightOverrideEnabled(env = process.env) {
|
|
304
|
+
return /^(1|true|yes|on)$/i.test((env.EXE_PREFLIGHT_OVERRIDE ?? "").trim());
|
|
305
|
+
}
|
|
306
|
+
function assertPreflightGate(report, options = {}) {
|
|
307
|
+
const ctx = options.context ? `[${options.context}] ` : "";
|
|
308
|
+
const log = options.log ?? ((m) => console.log(m));
|
|
309
|
+
const warn = options.warn ?? ((m) => console.warn(m));
|
|
310
|
+
const override = options.allowRed === true || isPreflightOverrideEnabled();
|
|
311
|
+
if (report.verdict === "GREEN") {
|
|
312
|
+
log(`${ctx}\u{1F7E2} preflight GREEN (pass ${report.passed} / warn ${report.warned} / fail ${report.failed}) \u2014 stack ${report.stackVersion}`);
|
|
313
|
+
return report;
|
|
314
|
+
}
|
|
315
|
+
if (report.verdict === "YELLOW") {
|
|
316
|
+
warn(`${ctx}\u{1F7E0} preflight YELLOW (${report.warned} warning(s)) \u2014 stack ${report.stackVersion}. Proceeding; warnings are non-blocking:`);
|
|
317
|
+
warn(formatPreflightFindings(report));
|
|
318
|
+
return report;
|
|
319
|
+
}
|
|
320
|
+
const findings = formatPreflightFindings(report);
|
|
321
|
+
if (override) {
|
|
322
|
+
warn(`${ctx}\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557`);
|
|
323
|
+
warn(`${ctx}\u2551 \u26A0\uFE0F BREAK-GLASS: preflight is RED but override is engaged \u2551`);
|
|
324
|
+
warn(`${ctx}\u2551 A RED manifest is shipping INTENTIONALLY. This is logged. \u2551`);
|
|
325
|
+
warn(`${ctx}\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D`);
|
|
326
|
+
warn(`${ctx}RED findings (overridden):
|
|
327
|
+
${findings}`);
|
|
328
|
+
warn(`${ctx}Override via: ${options.allowRed === true ? "--allow-red flag" : "EXE_PREFLIGHT_OVERRIDE env"}. Remediate the manifest immediately after.`);
|
|
329
|
+
return report;
|
|
330
|
+
}
|
|
331
|
+
throw new Error(
|
|
332
|
+
`${ctx}Preflight gate FAILED (RED) \u2014 stack ${report.stackVersion}. Company procedure: no manifest published or stack-update applied until preflight is GREEN.
|
|
333
|
+
${findings}
|
|
334
|
+
Break-glass: re-run with --allow-red or EXE_PREFLIGHT_OVERRIDE=1 to ship a RED manifest intentionally (LOUDLY logged).`
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
export {
|
|
339
|
+
resolveDefaultManifest,
|
|
340
|
+
resolveStack,
|
|
341
|
+
checkDigestPinning,
|
|
342
|
+
checkNoLatest,
|
|
343
|
+
checkDependencyClosure,
|
|
344
|
+
checkNoHardcodedHost,
|
|
345
|
+
checkEnvContract,
|
|
346
|
+
checkNoHostNet,
|
|
347
|
+
runPreflight,
|
|
348
|
+
runPreflightOnManifest,
|
|
349
|
+
assertPreflightGate
|
|
350
|
+
};
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import {
|
|
2
2
|
cleanupCardsForMemories,
|
|
3
3
|
cleanupGraphLinksForMemories
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-FF5DA4XP.js";
|
|
5
5
|
import {
|
|
6
6
|
resolveAnthropicAuth
|
|
7
7
|
} from "./chunk-PROQW3NL.js";
|
|
8
8
|
import {
|
|
9
9
|
vectorToBlob
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-WBJXJ2R4.js";
|
|
11
11
|
import {
|
|
12
12
|
extractKeywords,
|
|
13
13
|
keywordsToString
|
|
14
14
|
} from "./chunk-CHCA3ZM2.js";
|
|
15
15
|
import {
|
|
16
16
|
isCoordinatorName
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-SCYWCWKY.js";
|
|
18
18
|
|
|
19
19
|
// src/lib/consolidation.ts
|
|
20
20
|
import { randomUUID } from "crypto";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
loadLicense
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-JV225JBX.js";
|
|
4
4
|
|
|
5
5
|
// src/lib/stack-update.ts
|
|
6
6
|
import { execFileSync, spawnSync } from "child_process";
|
|
@@ -902,6 +902,65 @@ function refreshPackagedInitSql(dest, created = []) {
|
|
|
902
902
|
function refreshPackagedErpNginxConf(dest, created = []) {
|
|
903
903
|
copyTemplate("deploy/compose/erp-nginx/nginx.conf", dest, created, { overwrite: true });
|
|
904
904
|
}
|
|
905
|
+
function ensureOverlayDir(dest, actions = []) {
|
|
906
|
+
try {
|
|
907
|
+
if (existsSync(dest) && !lstatSync(dest).isDirectory()) {
|
|
908
|
+
rmSync(dest, { force: true });
|
|
909
|
+
}
|
|
910
|
+
if (!existsSync(dest)) {
|
|
911
|
+
mkdirSync(dest, { recursive: true, mode: 493 });
|
|
912
|
+
actions.push("create_overlay_dir");
|
|
913
|
+
}
|
|
914
|
+
} catch (err) {
|
|
915
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
916
|
+
console.warn(`[stack-update] Could not ensure overlay dir ${dest} (${reason}); customer overlays may not mount.`);
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
function listOverlayFiles(overlayDir) {
|
|
920
|
+
try {
|
|
921
|
+
if (!existsSync(overlayDir) || !lstatSync(overlayDir).isDirectory()) return [];
|
|
922
|
+
return readdirSync(overlayDir).filter((name) => {
|
|
923
|
+
try {
|
|
924
|
+
return lstatSync(path.join(overlayDir, name)).isFile();
|
|
925
|
+
} catch {
|
|
926
|
+
return false;
|
|
927
|
+
}
|
|
928
|
+
}).sort();
|
|
929
|
+
} catch {
|
|
930
|
+
return [];
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
function composeMountsOverlay(composeRaw) {
|
|
934
|
+
return /\.\/overlay:\/data\/overlay/.test(composeRaw);
|
|
935
|
+
}
|
|
936
|
+
function applyGatewayOverlays(stackDir, composeRaw, exec) {
|
|
937
|
+
const overlayDir = path.join(stackDir, "overlay");
|
|
938
|
+
const overlayFiles = listOverlayFiles(overlayDir);
|
|
939
|
+
if (overlayFiles.length === 0) {
|
|
940
|
+
return { applied: false, warned: false, overlayFiles };
|
|
941
|
+
}
|
|
942
|
+
let warned = false;
|
|
943
|
+
if (!composeMountsOverlay(composeRaw)) {
|
|
944
|
+
console.warn(
|
|
945
|
+
`[stack-update] \u26A0 OVERLAY WARNING (bug 02bc1bb8): ${overlayFiles.length} customer overlay file(s) found in ${overlayDir} (${overlayFiles.join(", ")}), but the live docker-compose.yml does NOT bind-mount ./overlay into the gateway. These overlays will be SILENTLY DROPPED on the next container recreate. Add '- ./overlay:/data/overlay:ro' to the exe-gateway service volumes and re-deploy.`
|
|
946
|
+
);
|
|
947
|
+
warned = true;
|
|
948
|
+
}
|
|
949
|
+
const addRoutes = path.join(stackDir, "add-routes.sh");
|
|
950
|
+
if (!existsSync(addRoutes)) {
|
|
951
|
+
console.warn(`[stack-update] \u26A0 overlay re-apply hook missing at ${addRoutes}; overlays NOT re-applied. Re-run bootstrap.`);
|
|
952
|
+
return { applied: false, warned: true, overlayFiles };
|
|
953
|
+
}
|
|
954
|
+
try {
|
|
955
|
+
console.log(`[stack-update] Re-applying ${overlayFiles.length} customer overlay file(s) via add-routes.sh (bug 02bc1bb8)\u2026`);
|
|
956
|
+
exec("bash", [addRoutes], { cwd: stackDir });
|
|
957
|
+
return { applied: true, warned, overlayFiles };
|
|
958
|
+
} catch (err) {
|
|
959
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
960
|
+
console.warn(`[stack-update] \u26A0 Overlay re-apply failed (non-fatal): ${reason}. Overlays remain at ${overlayDir}; apply manually with: bash ${addRoutes}`);
|
|
961
|
+
return { applied: false, warned: true, overlayFiles };
|
|
962
|
+
}
|
|
963
|
+
}
|
|
905
964
|
var BOOTSTRAP_OVERWRITTEN_CONFIG_FILES = [
|
|
906
965
|
"init-db.sql",
|
|
907
966
|
"erp-nginx/nginx.conf",
|
|
@@ -1009,6 +1068,7 @@ function hydrateEnv(raw, opts) {
|
|
|
1009
1068
|
else if (key.endsWith("_PASSWORD")) replacements[key] = randomSecret(24);
|
|
1010
1069
|
else if (key.endsWith("_TOKEN")) replacements[key] = randomHexSecret(32);
|
|
1011
1070
|
else if (key.endsWith("_SECRET") || key.endsWith("_SALT")) replacements[key] = randomSecret(32);
|
|
1071
|
+
else if (key === "EXE_MONITOR_KEY") replacements[key] = randomSecret(32);
|
|
1012
1072
|
else if (key.endsWith("_KEY") && key !== "EXE_LICENSE_KEY") continue;
|
|
1013
1073
|
}
|
|
1014
1074
|
if (domain) next = next.replaceAll("CHANGEME_DOMAIN", domain);
|
|
@@ -1016,44 +1076,23 @@ function hydrateEnv(raw, opts) {
|
|
|
1016
1076
|
const remaining = [...parseEnv(next).entries()].filter(([, value]) => /CHANGEME/.test(value)).map(([key, value]) => `${key}=${value}`);
|
|
1017
1077
|
return { raw: next, hadPlaceholders: /CHANGEME/.test(raw), remaining: [...new Set(remaining)] };
|
|
1018
1078
|
}
|
|
1019
|
-
async function pairMonitorAgent(hubUrl,
|
|
1020
|
-
if (!hubUrl
|
|
1021
|
-
return { paired: false, error: "Missing hubUrl
|
|
1079
|
+
async function pairMonitorAgent(hubUrl, _licenseKey, _domain, envFile) {
|
|
1080
|
+
if (!hubUrl) {
|
|
1081
|
+
return { paired: false, error: "Missing hubUrl for monitor pairing check" };
|
|
1022
1082
|
}
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
const
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
},
|
|
1031
|
-
body: JSON.stringify({ name: domain, host: domain, port: 45876 }),
|
|
1032
|
-
signal: AbortSignal.timeout(15e3)
|
|
1033
|
-
});
|
|
1034
|
-
if (!res.ok) {
|
|
1035
|
-
const body = await res.text().catch(() => "");
|
|
1036
|
-
return { paired: false, error: `Monitor hub returned HTTP ${res.status}: ${body}` };
|
|
1037
|
-
}
|
|
1038
|
-
const data = await res.json();
|
|
1039
|
-
if (!data.token || !data.key) {
|
|
1040
|
-
return { paired: false, error: "Monitor hub response missing token or key" };
|
|
1041
|
-
}
|
|
1042
|
-
if (existsSync(envFile)) {
|
|
1043
|
-
const envRaw = readFileSync(envFile, "utf8");
|
|
1044
|
-
const patched = patchEnv(envRaw, {
|
|
1045
|
-
MONITOR_AGENT_TOKEN: data.token,
|
|
1046
|
-
MONITOR_AGENT_KEY: data.key
|
|
1047
|
-
});
|
|
1048
|
-
if (patched !== envRaw) {
|
|
1049
|
-
writeSecretFile(envFile, patched);
|
|
1050
|
-
}
|
|
1083
|
+
if (existsSync(envFile)) {
|
|
1084
|
+
const envRaw = readFileSync(envFile, "utf8");
|
|
1085
|
+
const parsed = parseEnv(envRaw);
|
|
1086
|
+
const token = (parsed.get("MONITOR_AGENT_TOKEN") ?? "").trim();
|
|
1087
|
+
const key = (parsed.get("MONITOR_AGENT_KEY") ?? "").trim();
|
|
1088
|
+
if (token && !/CHANGEME/.test(token) && key && !/CHANGEME/.test(key)) {
|
|
1089
|
+
return { paired: true, systemName: "(existing credentials)" };
|
|
1051
1090
|
}
|
|
1052
|
-
return { paired: true, systemName: data.name ?? domain };
|
|
1053
|
-
} catch (err) {
|
|
1054
|
-
const reason = err instanceof Error ? err.message : String(err);
|
|
1055
|
-
return { paired: false, error: `Monitor hub unreachable: ${reason}` };
|
|
1056
1091
|
}
|
|
1092
|
+
return {
|
|
1093
|
+
paired: false,
|
|
1094
|
+
error: "The monitor hub (Beszel) does not expose a REST registration endpoint. Pair the agent manually: open the hub UI \u2192 Settings \u2192 Add System, then write the generated MONITOR_AGENT_TOKEN and MONITOR_AGENT_KEY into " + envFile
|
|
1095
|
+
};
|
|
1057
1096
|
}
|
|
1058
1097
|
function bootstrapStackHost(options) {
|
|
1059
1098
|
const exec = options.exec ?? defaultExec;
|
|
@@ -1098,6 +1137,15 @@ function bootstrapStackHost(options) {
|
|
|
1098
1137
|
path.join(path.dirname(options.composeFile), "cloudflared", "config.yml.example"),
|
|
1099
1138
|
createdFiles
|
|
1100
1139
|
);
|
|
1140
|
+
ensureOverlayDir(path.join(path.dirname(options.composeFile), "overlay"), actions);
|
|
1141
|
+
for (const helper of ["gateway-watchdog.sh", "add-routes.sh"]) {
|
|
1142
|
+
const dest = path.join(path.dirname(options.composeFile), helper);
|
|
1143
|
+
copyTemplate(`deploy/compose/${helper}`, dest, createdFiles, { overwrite: true });
|
|
1144
|
+
try {
|
|
1145
|
+
chmodSync(dest, 493);
|
|
1146
|
+
} catch {
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1101
1149
|
let envHadPlaceholders = false;
|
|
1102
1150
|
let envRemainingPlaceholders = [];
|
|
1103
1151
|
if (existsSync(options.envFile)) {
|
|
@@ -1504,7 +1552,7 @@ async function runStackUpdate(options) {
|
|
|
1504
1552
|
console.log(`[stack-update] Monitor agent paired: ${result.systemName}`);
|
|
1505
1553
|
} else {
|
|
1506
1554
|
console.warn(
|
|
1507
|
-
`[stack-update] \u26A0\uFE0F Monitor agent pairing
|
|
1555
|
+
`[stack-update] \u26A0\uFE0F Monitor agent pairing INCOMPLETE \u2014 the agent is enabled (COMPOSE_PROFILES=monitor-agent) but has no usable TOKEN/KEY, so it will NOT report fleet health. ${result.error}`
|
|
1508
1556
|
);
|
|
1509
1557
|
}
|
|
1510
1558
|
}
|
|
@@ -1663,6 +1711,13 @@ ${lines.join("\n")}`
|
|
|
1663
1711
|
} catch {
|
|
1664
1712
|
}
|
|
1665
1713
|
const updates = { ...migratedEnvVars, ...Object.fromEntries(plan.changes.map((c) => [c.key, c.after])) };
|
|
1714
|
+
{
|
|
1715
|
+
const currentMonitorKey = parseEnv(readFileSync(options.envFile, "utf8")).get("EXE_MONITOR_KEY") ?? "";
|
|
1716
|
+
if (!currentMonitorKey.trim() || /CHANGEME/.test(currentMonitorKey)) {
|
|
1717
|
+
updates.EXE_MONITOR_KEY = randomSecret(32);
|
|
1718
|
+
console.log("[stack-update] Generated EXE_MONITOR_KEY (bug 84d9a74a: was missing/empty \u2014 monitor error ingestion was failing 503)");
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1666
1721
|
const currentEnvMap = parseEnv(envRaw);
|
|
1667
1722
|
const manifestExeOsPin = updates.EXE_OS_IMAGE_TAG ?? currentEnvMap.get("EXE_OS_IMAGE_TAG");
|
|
1668
1723
|
const overrideInFile = /^\s*EXE_OS_SERVER_IMAGE_TAG\s*=/m.test(envRaw);
|
|
@@ -1956,6 +2011,13 @@ ${lines.join("\n")}`
|
|
|
1956
2011
|
endReleasePost();
|
|
1957
2012
|
}
|
|
1958
2013
|
}
|
|
2014
|
+
try {
|
|
2015
|
+
const liveCompose = existsSync(options.composeFile) ? readFileSync(options.composeFile, "utf8") : composeRaw;
|
|
2016
|
+
applyGatewayOverlays(stackDir, liveCompose, exec);
|
|
2017
|
+
} catch (overlayErr) {
|
|
2018
|
+
const reason = overlayErr instanceof Error ? overlayErr.message : String(overlayErr);
|
|
2019
|
+
console.warn(`[stack-update] \u26A0 Overlay re-apply step failed (non-fatal): ${reason}`);
|
|
2020
|
+
}
|
|
1959
2021
|
for (const serviceName of Object.keys(plan.release.services)) {
|
|
1960
2022
|
updateServiceStatus(lockFile, serviceName, "completed", now);
|
|
1961
2023
|
}
|
|
@@ -2488,6 +2550,7 @@ export {
|
|
|
2488
2550
|
assertProductionDeployGate,
|
|
2489
2551
|
assertBreakingChangesAllowed,
|
|
2490
2552
|
verifyManifestImagesAvailable,
|
|
2553
|
+
applyGatewayOverlays,
|
|
2491
2554
|
backupStackConfig,
|
|
2492
2555
|
pairMonitorAgent,
|
|
2493
2556
|
bootstrapStackHost,
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
dispatchTaskToEmployee,
|
|
3
3
|
markTaskNotificationsRead,
|
|
4
4
|
notifyTaskDone
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-TN2YCFQE.js";
|
|
6
6
|
import {
|
|
7
7
|
cleanupReviewFile,
|
|
8
8
|
createTaskCore,
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
sessionScopeFilter,
|
|
12
12
|
updateTaskStatus,
|
|
13
13
|
writeNotification
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-HED6D5KR.js";
|
|
15
15
|
import {
|
|
16
16
|
orgBus
|
|
17
17
|
} from "./chunk-MP2AFCGL.js";
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
getClient,
|
|
20
20
|
getCoordinatorName,
|
|
21
21
|
isCoordinatorName
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-SCYWCWKY.js";
|
|
23
23
|
import {
|
|
24
24
|
EXE_AI_DIR
|
|
25
25
|
} from "./chunk-DFIOOK5R.js";
|
|
@@ -61,7 +61,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
61
61
|
}
|
|
62
62
|
if (unblockedRows.rows.length > 0 && !process.env.VITEST) {
|
|
63
63
|
try {
|
|
64
|
-
const { dispatchTaskToEmployee: dispatchTaskToEmployee2 } = await import("./tasks-notify-
|
|
64
|
+
const { dispatchTaskToEmployee: dispatchTaskToEmployee2 } = await import("./tasks-notify-LAIU7UUI.js");
|
|
65
65
|
const dispatched = /* @__PURE__ */ new Set();
|
|
66
66
|
for (const ur of unblockedRows.rows) {
|
|
67
67
|
const assignee = String(ur.assigned_to);
|
|
@@ -333,7 +333,7 @@ async function updateTask(input) {
|
|
|
333
333
|
await markTaskNotificationsRead(taskFile);
|
|
334
334
|
if (isCompletionReviewTransition && !isCoordinator && row.reviewer) {
|
|
335
335
|
try {
|
|
336
|
-
const { writeNotification: writeNotification2 } = await import("./notifications-
|
|
336
|
+
const { writeNotification: writeNotification2 } = await import("./notifications-UP7WQGG4.js");
|
|
337
337
|
const reviewer = String(row.reviewer);
|
|
338
338
|
await writeNotification2({
|
|
339
339
|
agentId: reviewer,
|