@synergenius/flow-weaver 0.33.1 → 0.44.0
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/LICENSE +201 -226
- package/NOTICE +13 -0
- package/README.md +152 -244
- package/dist/agent/agent-loop.d.ts +4 -2
- package/dist/agent/cli-session.d.ts +3 -3
- package/dist/agent/cli-session.js +511 -438
- package/dist/agent/cli-spawn-config.d.ts +4 -4
- package/dist/agent/gate.d.ts +164 -0
- package/dist/agent/index.d.ts +4 -4
- package/dist/agent/index.js +174 -28
- package/dist/agent/mcp-bridge.d.ts +1 -1
- package/dist/agent/mcp-bridge.js +114 -128
- package/dist/agent/mcp-tool-server.d.ts +2 -2
- package/dist/agent/mcp-tool-server.js +147 -189
- package/dist/agent/profiles.d.ts +69 -0
- package/dist/agent/providers/anthropic.d.ts +2 -4
- package/dist/agent/providers/claude-cli.d.ts +4 -5
- package/dist/agent/providers/openai-compat.d.ts +8 -1
- package/dist/agent/streaming.d.ts +2 -2
- package/dist/agent/types.d.ts +16 -10
- package/dist/api/builder.d.ts +3 -3
- package/dist/api/compile.d.ts +9 -4
- package/dist/api/durable-validation.d.ts +15 -0
- package/dist/api/generate-in-place.d.ts +21 -19
- package/dist/api/generate.d.ts +21 -16
- package/dist/api/generated-module/local-node-functions.d.ts +24 -0
- package/dist/api/generated-module/module-format.d.ts +23 -0
- package/dist/api/generated-module/module-writer.d.ts +34 -0
- package/dist/api/generated-module/node-type-imports.d.ts +39 -0
- package/dist/api/generated-module/preamble.d.ts +16 -0
- package/dist/api/generated-module/workflow-functions.d.ts +29 -0
- package/dist/api/graph-identity.d.ts +39 -0
- package/dist/api/helpers.d.ts +4 -60
- package/dist/api/in-place/built-in-nodes.d.ts +18 -0
- package/dist/api/in-place/function-body.d.ts +21 -0
- package/dist/api/in-place/fw-imports.d.ts +30 -0
- package/dist/api/in-place/macro-coverage.d.ts +14 -0
- package/dist/api/in-place/node-type-functions.d.ts +18 -0
- package/dist/api/in-place/node-type-jsdoc.d.ts +20 -0
- package/dist/api/in-place/orphaned-node-types.d.ts +16 -0
- package/dist/api/in-place/runtime-section.d.ts +16 -0
- package/dist/api/in-place/signature.d.ts +47 -0
- package/dist/api/in-place/source-file.d.ts +19 -0
- package/dist/api/in-place/workflow-jsdoc.d.ts +19 -0
- package/dist/api/index.d.ts +18 -21
- package/dist/api/index.js +259 -53
- package/dist/api/inline-engine.generated.d.ts +3 -0
- package/dist/api/inline-runtime.d.ts +10 -2
- package/dist/api/manipulation/connections.d.ts +1 -1
- package/dist/api/manipulation/index.d.ts +8 -8
- package/dist/api/manipulation/node-types.d.ts +1 -1
- package/dist/api/manipulation/nodes.d.ts +3 -13
- package/dist/api/manipulation/ports.d.ts +1 -1
- package/dist/api/manipulation/scopes.d.ts +1 -1
- package/dist/api/manipulation/workflow.d.ts +1 -1
- package/dist/api/modify-operation.d.ts +0 -2
- package/dist/api/parse.d.ts +22 -1
- package/dist/api/port-references.d.ts +66 -0
- package/dist/api/query.d.ts +18 -2
- package/dist/api/templates.d.ts +1 -1
- package/dist/api/transform.d.ts +1 -1
- package/dist/api/validate.d.ts +1 -1
- package/dist/api/validation-registry.d.ts +2 -2
- package/dist/artifacts/brief.d.ts +43 -0
- package/dist/artifacts/index.d.ts +31 -0
- package/dist/artifacts/pdf.d.ts +19 -0
- package/dist/ast/builder.d.ts +3 -3
- package/dist/ast/index.d.ts +4 -4
- package/dist/ast/index.js +60 -5
- package/dist/ast/serialization-node.d.ts +1 -1
- package/dist/ast/serialization.d.ts +1 -1
- package/dist/ast/types.d.ts +82 -291
- package/dist/ast/workflow-utils.d.ts +4 -4
- package/dist/built-in-nodes/coercion-types.d.ts +1 -1
- package/dist/built-in-nodes/delay.d.ts +3 -1
- package/dist/built-in-nodes/generated-registry.d.ts +1 -1
- package/dist/built-in-nodes/index.d.ts +6 -5
- package/dist/built-in-nodes/index.js +79 -5
- package/dist/built-in-nodes/invoke-workflow.d.ts +2 -1
- package/dist/built-in-nodes/mock-types.d.ts +45 -14
- package/dist/built-in-nodes/sleep.d.ts +12 -0
- package/dist/built-in-nodes/wait-for-agent.d.ts +2 -1
- package/dist/built-in-nodes/wait-for-event.d.ts +2 -1
- package/dist/chevrotain-parser/coerce-parser.d.ts +1 -1
- package/dist/chevrotain-parser/connect-parser.d.ts +1 -1
- package/dist/chevrotain-parser/grammar-diagrams.d.ts +1 -1
- package/dist/chevrotain-parser/index.d.ts +20 -23
- package/dist/chevrotain-parser/node-parser.d.ts +7 -8
- package/dist/chevrotain-parser/parse-line.d.ts +54 -0
- package/dist/chevrotain-parser/tokens.d.ts +0 -17
- package/dist/chevrotain-parser/trigger-cancel-parser.d.ts +6 -1
- package/dist/chunks/chunk-4EUSHAY6.js +298 -0
- package/dist/chunks/chunk-4KDUEOFD.js +197 -0
- package/dist/chunks/chunk-5Y3AIUWY.js +911 -0
- package/dist/chunks/chunk-6KMVP6AU.js +20 -0
- package/dist/chunks/chunk-6PNJORFL.js +128 -0
- package/dist/chunks/chunk-7AAQNGRH.js +2001 -0
- package/dist/chunks/chunk-7YYKTPJ5.js +22705 -0
- package/dist/chunks/chunk-A5MBOFVY.js +33 -0
- package/dist/chunks/chunk-AXNQRVJB.js +18 -0
- package/dist/chunks/chunk-B6HQ6YS5.js +572 -0
- package/dist/chunks/chunk-CY3RNVML.js +296 -0
- package/dist/chunks/chunk-CYHYDLV4.js +653 -0
- package/dist/chunks/chunk-CZBCCK4Y.js +82 -0
- package/dist/chunks/chunk-DCMGQP5V.js +505 -0
- package/dist/chunks/chunk-DIAA66GL.js +72 -0
- package/dist/chunks/chunk-DUO6H5PR.js +20 -0
- package/dist/chunks/chunk-E5WN7IGO.js +125 -0
- package/dist/chunks/chunk-EFGFXXQF.js +184 -0
- package/dist/chunks/chunk-F3RTZV2A.js +2811 -0
- package/dist/chunks/chunk-FMTLP5ZX.js +1397 -0
- package/dist/chunks/chunk-HKZLQJDV.js +79 -0
- package/dist/chunks/chunk-INDEVYEC.js +146 -0
- package/dist/chunks/chunk-JQTF4R5L.js +390 -0
- package/dist/chunks/chunk-KMCAYNUM.js +331 -0
- package/dist/chunks/chunk-KMWCCOUJ.js +1444 -0
- package/dist/chunks/chunk-KU3CLJI2.js +338 -0
- package/dist/chunks/chunk-NDSTGMKR.js +3300 -0
- package/dist/chunks/chunk-NQNLUAPO.js +505 -0
- package/dist/chunks/chunk-O2U2LO57.js +466 -0
- package/dist/chunks/chunk-OOQG7UPP.js +457 -0
- package/dist/chunks/chunk-OZQAW5ZG.js +112 -0
- package/dist/chunks/chunk-P2CVDGHR.js +487 -0
- package/dist/chunks/chunk-P44RIZ7C.js +592 -0
- package/dist/chunks/chunk-PEZCT37T.js +1475 -0
- package/dist/chunks/chunk-PKWSAW6O.js +152 -0
- package/dist/chunks/chunk-Q6MDDZMT.js +4373 -0
- package/dist/chunks/chunk-QDE4Z7KW.js +6 -0
- package/dist/chunks/chunk-QJDAVSNY.js +8 -0
- package/dist/chunks/chunk-QYPORSLO.js +7552 -0
- package/dist/chunks/chunk-R4QGNK34.js +183 -0
- package/dist/chunks/chunk-REEYC73I.js +3002 -0
- package/dist/chunks/chunk-SEWDMQ6Q.js +46 -0
- package/dist/chunks/chunk-TAIUWKZB.js +3057 -0
- package/dist/chunks/chunk-TN4YZ3O7.js +196 -0
- package/dist/chunks/chunk-TRMFB43C.js +143 -0
- package/dist/chunks/chunk-VO4QXG2X.js +1016 -0
- package/dist/chunks/chunk-VP2P3BPA.js +1002 -0
- package/dist/chunks/chunk-VQ7H33J4.js +2176 -0
- package/dist/chunks/chunk-VWA7MCV2.js +105 -0
- package/dist/chunks/chunk-XAV22WD7.js +253 -0
- package/dist/chunks/chunk-YSPBPZDV.js +148 -0
- package/dist/chunks/chunk-ZKEGXO73.js +282 -0
- package/dist/chunks/chunk-ZMGN6F3S.js +4256 -0
- package/dist/chunks/grammar-diagrams-AI7VDXOM.js +14 -0
- package/dist/chunks/index.min-ECJD6GZC.js +37 -0
- package/dist/chunks/manifest-EPTR5MYH.js +21 -0
- package/dist/chunks/openapi-document-6TCGVDUW.js +69 -0
- package/dist/chunks/registry-K2U5KUTY.js +23 -0
- package/dist/chunks/serialization-node-UGY6CFH6.js +38 -0
- package/dist/chunks/validate-GEKSZPIN.js +24 -0
- package/dist/chunks/validation-registry-IQZX3UE3.js +7 -0
- package/dist/chunks/validator-VXFHLPDW.js +26 -0
- package/dist/chunks/workflow-executor-KBGFKHNR.js +36 -0
- package/dist/cli/commands/agents.d.ts +16 -0
- package/dist/cli/commands/artifact.d.ts +10 -0
- package/dist/cli/commands/compile.d.ts +0 -30
- package/dist/cli/commands/console.d.ts +32 -0
- package/dist/cli/commands/context.d.ts +0 -3
- package/dist/cli/commands/describe.js +386 -352
- package/dist/cli/commands/dev.d.ts +9 -4
- package/dist/cli/commands/diagram.d.ts +3 -5
- package/dist/cli/commands/doctor.d.ts +27 -4
- package/dist/cli/commands/doctor.js +3307 -633
- package/dist/cli/commands/export.d.ts +6 -4
- package/dist/cli/commands/grammar.d.ts +0 -3
- package/dist/cli/commands/implement.d.ts +1 -1
- package/dist/cli/commands/init/agent-handoff.d.ts +28 -0
- package/dist/cli/commands/init/config.d.ts +3 -0
- package/dist/cli/commands/init/files.d.ts +12 -0
- package/dist/cli/commands/init/naming.d.ts +13 -0
- package/dist/cli/commands/init/output.d.ts +12 -0
- package/dist/cli/commands/init/setup.d.ts +21 -0
- package/dist/cli/commands/init/types.d.ts +53 -0
- package/dist/cli/commands/init-personas.d.ts +9 -1
- package/dist/cli/commands/init.d.ts +24 -88
- package/dist/cli/commands/market.d.ts +1 -1
- package/dist/cli/commands/mcp-setup.d.ts +1 -4
- package/dist/cli/commands/modify.d.ts +0 -5
- package/dist/cli/commands/openapi.d.ts +9 -11
- package/dist/cli/commands/run.d.ts +0 -4
- package/dist/cli/commands/serve.d.ts +24 -26
- package/dist/cli/commands/status.d.ts +1 -1
- package/dist/cli/exports.d.ts +3 -4
- package/dist/cli/exports.js +124 -23
- package/dist/cli/flow-weaver-agents-H653446S.mjs +81 -0
- package/dist/cli/flow-weaver-artifact-WBFYTW76.mjs +58 -0
- package/dist/cli/flow-weaver-chunk-265CEK74.mjs +2 -0
- package/dist/cli/flow-weaver-chunk-2C7PZCV5.mjs +186 -0
- package/dist/cli/flow-weaver-chunk-3BSYCWPL.mjs +3109 -0
- package/dist/cli/flow-weaver-chunk-3MKUQP3D.mjs +547 -0
- package/dist/cli/flow-weaver-chunk-3ZLMPQTH.mjs +27 -0
- package/dist/cli/flow-weaver-chunk-4DVY73ML.mjs +280 -0
- package/dist/cli/flow-weaver-chunk-54M4KNEL.mjs +200 -0
- package/dist/cli/flow-weaver-chunk-55QMUZIA.mjs +256 -0
- package/dist/cli/flow-weaver-chunk-6ZUJ7LMX.mjs +2003 -0
- package/dist/cli/flow-weaver-chunk-7ET6N32V.mjs +419 -0
- package/dist/cli/flow-weaver-chunk-7G63RR4K.mjs +8880 -0
- package/dist/cli/flow-weaver-chunk-A6M5K3W7.mjs +4418 -0
- package/dist/cli/flow-weaver-chunk-AA3NATMX.mjs +154 -0
- package/dist/cli/flow-weaver-chunk-APX6MQ5A.mjs +5652 -0
- package/dist/cli/flow-weaver-chunk-BHCC5FE5.mjs +17 -0
- package/dist/cli/flow-weaver-chunk-DM4BBRRU.mjs +35 -0
- package/dist/cli/flow-weaver-chunk-DRN7KPWY.mjs +168 -0
- package/dist/cli/flow-weaver-chunk-EKWF27G6.mjs +23 -0
- package/dist/cli/flow-weaver-chunk-EM5HWQ4R.mjs +338 -0
- package/dist/cli/flow-weaver-chunk-FPPPU2DG.mjs +226 -0
- package/dist/cli/flow-weaver-chunk-GAQXZHNO.mjs +3005 -0
- package/dist/cli/flow-weaver-chunk-GBUNNP33.mjs +1329 -0
- package/dist/cli/flow-weaver-chunk-HMAKDE55.mjs +636 -0
- package/dist/cli/flow-weaver-chunk-HQ3ZAN44.mjs +367 -0
- package/dist/cli/flow-weaver-chunk-I4APCXYN.mjs +21442 -0
- package/dist/cli/flow-weaver-chunk-IJJ4JXTL.mjs +2297 -0
- package/dist/cli/flow-weaver-chunk-J7EPZW2I.mjs +33 -0
- package/dist/cli/flow-weaver-chunk-KOEMJHLG.mjs +83 -0
- package/dist/cli/flow-weaver-chunk-M6CBWQKJ.mjs +271 -0
- package/dist/cli/flow-weaver-chunk-MHJ4N652.mjs +1580 -0
- package/dist/cli/flow-weaver-chunk-MKAFQWLP.mjs +202 -0
- package/dist/cli/flow-weaver-chunk-MMV2XGD3.mjs +3291 -0
- package/dist/cli/flow-weaver-chunk-MSI2UJES.mjs +125 -0
- package/dist/cli/flow-weaver-chunk-N767YRAI.mjs +48 -0
- package/dist/cli/flow-weaver-chunk-NQKKWDGF.mjs +56 -0
- package/dist/cli/flow-weaver-chunk-NYZDQYFU.mjs +45 -0
- package/dist/cli/flow-weaver-chunk-OMVWSNHJ.mjs +14 -0
- package/dist/cli/flow-weaver-chunk-PUWZ47H5.mjs +2188 -0
- package/dist/cli/flow-weaver-chunk-QOPRVDES.mjs +46 -0
- package/dist/cli/flow-weaver-chunk-RIWE7YTR.mjs +216 -0
- package/dist/cli/flow-weaver-chunk-S63Q3JIU.mjs +789 -0
- package/dist/cli/flow-weaver-chunk-T7X5A23H.mjs +558 -0
- package/dist/cli/flow-weaver-chunk-TLV3VZUN.mjs +2 -0
- package/dist/cli/flow-weaver-chunk-TS5P3PIN.mjs +194 -0
- package/dist/cli/flow-weaver-chunk-U3EO3X4B.mjs +649 -0
- package/dist/cli/flow-weaver-chunk-UNFS3NQP.mjs +34 -0
- package/dist/cli/flow-weaver-chunk-V6DHWFQG.mjs +2811 -0
- package/dist/cli/flow-weaver-chunk-VXWNLYQU.mjs +9 -0
- package/dist/cli/flow-weaver-chunk-W3YNK26P.mjs +149 -0
- package/dist/cli/flow-weaver-chunk-XG2WKXYZ.mjs +324 -0
- package/dist/cli/flow-weaver-chunk-YLSRZWFK.mjs +2219 -0
- package/dist/cli/flow-weaver-chunk-YVPURWS6.mjs +40 -0
- package/dist/cli/flow-weaver-chunk-Z44JK332.mjs +582 -0
- package/dist/cli/flow-weaver-chunk-Z5ZJPNSY.mjs +517 -0
- package/dist/cli/flow-weaver-chunk-ZQALO25T.mjs +594 -0
- package/dist/cli/flow-weaver-compile-A2CABDSB.mjs +37 -0
- package/dist/cli/flow-weaver-console-6EBHDBTN.mjs +71 -0
- package/dist/cli/flow-weaver-context-ECLYANFF.mjs +17 -0
- package/dist/cli/flow-weaver-context-ZRUMW2KE.mjs +69 -0
- package/dist/cli/flow-weaver-create-WIP4EFNP.mjs +124 -0
- package/dist/cli/flow-weaver-deployment-WJJRK7NO.mjs +14 -0
- package/dist/cli/flow-weaver-describe-XDIFF5LI.mjs +41 -0
- package/dist/cli/flow-weaver-dev-LG2NSQAU.mjs +196 -0
- package/dist/cli/flow-weaver-diagram-3EVQARQE.mjs +52 -0
- package/dist/cli/flow-weaver-diagram-7LR7O4M2.mjs +44 -0
- package/dist/cli/flow-weaver-diff-USF7DJLU.mjs +85 -0
- package/dist/cli/flow-weaver-docs-QU2ZOXKH.mjs +96 -0
- package/dist/cli/flow-weaver-doctor-K447YWHR.mjs +43 -0
- package/dist/cli/flow-weaver-export-LAS6N56Y.mjs +140 -0
- package/dist/cli/flow-weaver-grammar-RIRUS3Q6.mjs +49 -0
- package/dist/cli/flow-weaver-grammar-diagrams-WXRTW5SK.mjs +15 -0
- package/dist/cli/flow-weaver-implement-5JG6BCFZ.mjs +114 -0
- package/dist/cli/flow-weaver-index.min-OR5SIHXD.mjs +39 -0
- package/dist/cli/flow-weaver-init-34YZNPQX.mjs +972 -0
- package/dist/cli/flow-weaver-init-personas-NOGZTQQ2.mjs +48 -0
- package/dist/cli/flow-weaver-manifest-4JNUSEB3.mjs +18 -0
- package/dist/cli/flow-weaver-market-WA4CKHKV.mjs +444 -0
- package/dist/cli/flow-weaver-mcp-setup-O26MGXSD.mjs +22 -0
- package/dist/cli/flow-weaver-migrate-VFVEUTPH.mjs +119 -0
- package/dist/cli/flow-weaver-modify-VM3JP5K6.mjs +97 -0
- package/dist/cli/flow-weaver-openapi-IRBWI3HM.mjs +71 -0
- package/dist/cli/flow-weaver-openapi-document-7N6YMPG3.mjs +30 -0
- package/dist/cli/flow-weaver-pack-commands-OS2QRXAK.mjs +146 -0
- package/dist/cli/flow-weaver-registry-5MLFRPPT.mjs +25 -0
- package/dist/cli/flow-weaver-run-DSTLSTHR.mjs +555 -0
- package/dist/cli/flow-weaver-serialization-node-LE6PCG4M.mjs +50 -0
- package/dist/cli/flow-weaver-serve-NVLCLSOW.mjs +192 -0
- package/dist/cli/flow-weaver-server-7IERLVXE.mjs +2925 -0
- package/dist/cli/flow-weaver-server-KWF4PXYC.mjs +19112 -0
- package/dist/cli/flow-weaver-status-75W2S3YB.mjs +138 -0
- package/dist/cli/flow-weaver-strip-AZPXUYVA.mjs +79 -0
- package/dist/cli/flow-weaver-templates-DSNMB6K7.mjs +72 -0
- package/dist/cli/flow-weaver-validate-A4UWDQN6.mjs +19 -0
- package/dist/cli/flow-weaver-validate-GRBZ5W4C.mjs +296 -0
- package/dist/cli/flow-weaver-validation-registry-BINGUNXX.mjs +9 -0
- package/dist/cli/flow-weaver-validator-TY56CI42.mjs +21 -0
- package/dist/cli/flow-weaver-watch-OPPERXL6.mjs +114 -0
- package/dist/cli/flow-weaver-workflow-executor-6AHYPXAN.mjs +26 -0
- package/dist/cli/flow-weaver.mjs +3557 -89498
- package/dist/cli/index.d.ts +5 -2
- package/dist/cli/pack-commands-needed.d.ts +12 -0
- package/dist/cli/program.d.ts +14 -0
- package/dist/cli/templates/ai/mock-provider.d.ts +1 -1
- package/dist/cli/templates/index.d.ts +1 -8
- package/dist/cli/templates/nodes/agent-router.d.ts +1 -1
- package/dist/cli/templates/nodes/aggregator.d.ts +1 -1
- package/dist/cli/templates/nodes/conversation-memory.d.ts +1 -1
- package/dist/cli/templates/nodes/http.d.ts +1 -1
- package/dist/cli/templates/nodes/human-approval.d.ts +1 -1
- package/dist/cli/templates/nodes/json-extractor.d.ts +1 -1
- package/dist/cli/templates/nodes/llm-call.d.ts +1 -1
- package/dist/cli/templates/nodes/prompt-template.d.ts +1 -1
- package/dist/cli/templates/nodes/rag-retriever.d.ts +1 -1
- package/dist/cli/templates/nodes/tool-executor.d.ts +1 -1
- package/dist/cli/templates/nodes/transformer.d.ts +1 -1
- package/dist/cli/templates/nodes/validator.d.ts +1 -1
- package/dist/cli/templates/shared/approval-types.d.ts +1 -1
- package/dist/cli/templates/shared/case.d.ts +14 -0
- package/dist/cli/templates/shared/llm-types.d.ts +4 -4
- package/dist/cli/templates/workflows/aggregator.d.ts +1 -1
- package/dist/cli/templates/workflows/ai-agent.d.ts +1 -1
- package/dist/cli/templates/workflows/ai-chat.d.ts +1 -1
- package/dist/cli/templates/workflows/ai-rag.d.ts +1 -1
- package/dist/cli/templates/workflows/ai-react.d.ts +1 -1
- package/dist/cli/templates/workflows/approval.d.ts +10 -0
- package/dist/cli/templates/workflows/conditional.d.ts +2 -2
- package/dist/cli/templates/workflows/error-handler.d.ts +2 -2
- package/dist/cli/templates/workflows/foreach.d.ts +1 -1
- package/dist/cli/templates/workflows/sequential.d.ts +1 -1
- package/dist/cli/templates/workflows/webhook.d.ts +1 -1
- package/dist/cli/utils/interactive.d.ts +6 -0
- package/dist/cli/utils/json-option.d.ts +8 -0
- package/dist/cli/utils/logger.d.ts +2 -0
- package/dist/compiler/durable-source-proof.d.ts +23 -0
- package/dist/compiler/executable-artifact.d.ts +29 -0
- package/dist/compiler/index.d.ts +4 -0
- package/dist/compiler/index.js +36 -0
- package/dist/console/app.js +106 -0
- package/dist/console/assets/synergenius.svg +18 -0
- package/dist/console/author.d.ts +32 -0
- package/dist/console/cli-catalog.d.ts +21 -0
- package/dist/console/cli-run.d.ts +35 -0
- package/dist/console/cli-run.js +46 -0
- package/dist/console/debug.d.ts +67 -0
- package/dist/console/diff-view.d.ts +96 -0
- package/dist/console/export.d.ts +36 -0
- package/dist/console/git.d.ts +27 -0
- package/dist/console/index.d.ts +4 -0
- package/dist/console/index.html +16 -0
- package/dist/console/index.js +203 -0
- package/dist/console/packs.d.ts +100 -0
- package/dist/console/request-guard.d.ts +10 -0
- package/dist/console/respond.d.ts +16 -0
- package/dist/console/router.d.ts +64 -0
- package/dist/console/routes-agents.d.ts +14 -0
- package/dist/console/routes-project.d.ts +3 -0
- package/dist/console/routes-runs.d.ts +3 -0
- package/dist/console/routes-services.d.ts +7 -0
- package/dist/console/routes-tools.d.ts +3 -0
- package/dist/console/runs.d.ts +69 -0
- package/dist/console/scan.d.ts +49 -0
- package/dist/console/schema.d.ts +74 -0
- package/dist/console/server.d.ts +34 -0
- package/dist/console/server.js +43264 -0
- package/dist/console/services.d.ts +119 -0
- package/dist/console/source.d.ts +23 -0
- package/dist/console/status.d.ts +68 -0
- package/dist/console/styles.css +1023 -0
- package/dist/console/terminals.d.ts +22 -0
- package/dist/console/workflow-view.d.ts +153 -0
- package/dist/constants.d.ts +5 -1
- package/dist/constants.js +56 -167
- package/dist/context/index.d.ts +10 -0
- package/dist/context/index.js +14 -191
- package/dist/coordinator/agent-gate.d.ts +107 -0
- package/dist/coordinator/bundle-digest.d.ts +20 -0
- package/dist/coordinator/clock-tick.d.ts +4 -0
- package/dist/coordinator/effect-receipts.d.ts +10 -0
- package/dist/coordinator/errors.d.ts +31 -0
- package/dist/coordinator/file-store.d.ts +3 -0
- package/dist/coordinator/gate-labeling.d.ts +33 -0
- package/dist/coordinator/gate-resolution.d.ts +28 -0
- package/dist/coordinator/index.d.ts +12 -0
- package/dist/coordinator/index.js +107 -0
- package/dist/coordinator/memory-store.d.ts +3 -0
- package/dist/coordinator/params.d.ts +18 -0
- package/dist/coordinator/run-annotations.d.ts +6 -0
- package/dist/coordinator/run-context.d.ts +11 -0
- package/dist/coordinator/run-ending.d.ts +5 -0
- package/dist/coordinator/run-outcome.d.ts +31 -0
- package/dist/coordinator/run-resume.d.ts +6 -0
- package/dist/coordinator/run-start.d.ts +4 -0
- package/dist/coordinator/run-store.d.ts +275 -0
- package/dist/coordinator/run-trace.d.ts +41 -0
- package/dist/coordinator/run-views.d.ts +12 -0
- package/dist/coordinator/runs-dir.d.ts +26 -0
- package/dist/coordinator/store.d.ts +67 -0
- package/dist/coordinator/time.d.ts +39 -0
- package/dist/coordinator/workflow-selection.d.ts +6 -0
- package/dist/deployment/index.d.ts +11 -16
- package/dist/deployment/index.js +541 -50
- package/dist/deployment/targets/base.d.ts +11 -13
- package/dist/deployment/targets/openapi-parts.d.ts +92 -0
- package/dist/diagram/arms.d.ts +70 -0
- package/dist/diagram/ascii-renderer.d.ts +1 -1
- package/dist/diagram/geometry.d.ts +22 -29
- package/dist/diagram/graph/bounds.d.ts +30 -0
- package/dist/diagram/graph/metrics.d.ts +21 -0
- package/dist/diagram/graph/nodes.d.ts +33 -0
- package/dist/diagram/graph/placement.d.ts +22 -0
- package/dist/diagram/graph/ports.d.ts +23 -0
- package/dist/diagram/graph/routing.d.ts +40 -0
- package/dist/diagram/graph/scopes.d.ts +30 -0
- package/dist/diagram/index.d.ts +15 -23
- package/dist/diagram/index.js +632 -111
- package/dist/diagram/labels.d.ts +12 -0
- package/dist/diagram/lanes.d.ts +93 -0
- package/dist/diagram/layout.d.ts +1 -1
- package/dist/diagram/material-symbols.d.ts +7 -0
- package/dist/diagram/orthogonal-router.d.ts +15 -51
- package/dist/diagram/process-view.d.ts +79 -0
- package/dist/diagram/spine.d.ts +25 -0
- package/dist/diagram/theme.d.ts +3 -5
- package/dist/diagram/types.d.ts +6 -20
- package/dist/diff/index.js +53 -9
- package/dist/diff/types.d.ts +1 -5
- package/dist/doc-metadata/extractors/annotations.d.ts +0 -4
- package/dist/doc-metadata/extractors/cli-commands.d.ts +2 -1
- package/dist/doc-metadata/extractors/core-metadata.d.ts +19 -4
- package/dist/doc-metadata/extractors/error-codes.d.ts +1 -1
- package/dist/doc-metadata/extractors/mcp-tools.d.ts +7 -3
- package/dist/doc-metadata/index.d.ts +1 -2
- package/dist/doc-metadata/index.js +2368 -9
- package/dist/doc-metadata/types.d.ts +7 -24
- package/dist/docs/guide.d.ts +33 -0
- package/dist/docs/index.d.ts +7 -4
- package/dist/docs/index.js +298 -298
- package/dist/docs/pack-topics.d.ts +18 -0
- package/dist/editor-completions/annotationValues.d.ts +1 -1
- package/dist/editor-completions/contextParser.d.ts +3 -2
- package/dist/editor-completions/dataTypes.d.ts +1 -1
- package/dist/editor-completions/goToDefinition.d.ts +1 -1
- package/dist/editor-completions/index.d.ts +8 -8
- package/dist/editor-completions/index.js +1244 -175
- package/dist/editor-completions/jsDocAnnotations.d.ts +1 -1
- package/dist/editor-completions/modifierCompletions.d.ts +1 -1
- package/dist/export/index.d.ts +7 -1
- package/dist/generated-branding.js +8 -21
- package/dist/generated-version.d.ts +1 -1
- package/dist/generated-version.js +7 -3
- package/dist/generator/annotation-generator.d.ts +84 -0
- package/dist/generator/async-detection.d.ts +1 -1
- package/dist/{body-generator.d.ts → generator/body-generator.d.ts} +7 -3
- package/dist/generator/code-utils.d.ts +15 -60
- package/dist/generator/connection-reads.d.ts +94 -0
- package/dist/generator/control-flow-plan.d.ts +74 -0
- package/dist/generator/control-flow.d.ts +1 -1
- package/dist/generator/exit-assembly.d.ts +11 -0
- package/dist/generator/index.d.ts +4 -5
- package/dist/generator/index.js +34 -11
- package/dist/generator/input-port-values.d.ts +22 -0
- package/dist/generator/{scope-function-generator.d.ts → node-arguments.d.ts} +76 -1
- package/dist/generator/node-invocation.d.ts +74 -0
- package/dist/{node-types-generator.d.ts → generator/node-types-generator.d.ts} +3 -2
- package/dist/generator/scope-closure.d.ts +23 -0
- package/dist/generator/scoped-child.d.ts +49 -0
- package/dist/{sugar-optimizer.d.ts → generator/sugar-optimizer.d.ts} +1 -1
- package/dist/generator/unified.d.ts +14 -2
- package/dist/generator/workflow-generator.d.ts +37 -0
- package/dist/index.d.ts +29 -26
- package/dist/index.js +1322 -92
- package/dist/jsdoc-port-sync/apply-diff.d.ts +15 -0
- package/dist/jsdoc-port-sync/callback-signature.d.ts +17 -0
- package/dist/jsdoc-port-sync/diff.d.ts +5 -7
- package/dist/jsdoc-port-sync/incomplete-lines.d.ts +41 -0
- package/dist/jsdoc-port-sync/index.d.ts +11 -11
- package/dist/jsdoc-port-sync/index.js +88 -45
- package/dist/jsdoc-port-sync/jsdoc-to-signature.d.ts +66 -0
- package/dist/jsdoc-port-sync/port-parser.d.ts +6 -24
- package/dist/jsdoc-port-sync/port-tag-placement.d.ts +55 -0
- package/dist/jsdoc-port-sync/scoped-ports.d.ts +21 -0
- package/dist/jsdoc-port-sync/signature-parser.d.ts +8 -1
- package/dist/jsdoc-port-sync/signature-to-jsdoc.d.ts +77 -0
- package/dist/jsdoc-port-sync/sync.d.ts +4 -1
- package/dist/marketplace/index.d.ts +5 -3
- package/dist/marketplace/index.js +62 -10
- package/dist/marketplace/install.d.ts +24 -0
- package/dist/marketplace/manifest.d.ts +1 -1
- package/dist/marketplace/npmrc.d.ts +24 -0
- package/dist/marketplace/registry.d.ts +46 -11
- package/dist/marketplace/types.d.ts +41 -24
- package/dist/marketplace/validator.d.ts +1 -1
- package/dist/mcp/debug-session.d.ts +2 -4
- package/dist/mcp/dist-imports.d.ts +12 -0
- package/dist/mcp/response-utils.d.ts +11 -9
- package/dist/mcp/server.d.ts +0 -1
- package/dist/mcp/tools-context.d.ts +11 -0
- package/dist/mcp/tools-diagram.d.ts +1 -2
- package/dist/mcp/tools-export.d.ts +2 -2
- package/dist/mcp/tools-marketplace.d.ts +1 -1
- package/dist/mcp/tools-run.d.ts +17 -0
- package/dist/mcp/tools-workflow.d.ts +3 -0
- package/dist/mcp/types.d.ts +4 -14
- package/dist/mcp/workflow-executor.d.ts +57 -19
- package/dist/npm-packages.d.ts +1 -1
- package/dist/npm-packages.js +10 -473
- package/dist/parser/annotation-parser.d.ts +88 -0
- package/dist/parser/annotation-suggestion.d.ts +23 -0
- package/dist/parser/durable-effect-contract.d.ts +10 -0
- package/dist/parser/expression-references.d.ts +101 -0
- package/dist/parser/external-node-types.d.ts +69 -0
- package/dist/{function-like.d.ts → parser/function-like.d.ts} +10 -3
- package/dist/parser/generated-sections.d.ts +25 -0
- package/dist/parser/import-resolution.d.ts +67 -0
- package/dist/parser/import-stub.d.ts +29 -0
- package/dist/parser/jsdoc/config-types.d.ts +209 -0
- package/dist/parser/jsdoc/graph-tags.d.ts +54 -0
- package/dist/parser/jsdoc/node-type-block.d.ts +23 -0
- package/dist/parser/jsdoc/parse-line-once.d.ts +11 -0
- package/dist/parser/jsdoc/port-tags.d.ts +42 -0
- package/dist/parser/jsdoc/runtime-tags.d.ts +65 -0
- package/dist/parser/jsdoc/signature-types.d.ts +52 -0
- package/dist/parser/jsdoc/workflow-block.d.ts +22 -0
- package/dist/parser/jsdoc-parser.d.ts +23 -0
- package/dist/parser/macro-expansion.d.ts +80 -0
- package/dist/parser/module-resolution.d.ts +38 -0
- package/dist/parser/node-inference.d.ts +39 -0
- package/dist/parser/npm-node-types.d.ts +24 -0
- package/dist/parser/pack-handlers.d.ts +12 -0
- package/dist/parser/path-data-resolution.d.ts +75 -0
- package/dist/parser/port-inference.d.ts +15 -0
- package/dist/{resolve-package-types.d.ts → parser/resolve-package-types.d.ts} +4 -0
- package/dist/parser/serialize-deploy-annotations.d.ts +19 -0
- package/dist/parser/tag-registry.d.ts +22 -0
- package/dist/parser/workflow-extraction.d.ts +39 -0
- package/dist/runtime/ExecutionContext.d.ts +87 -99
- package/dist/runtime/continuation-core.d.ts +147 -0
- package/dist/runtime/continuation.d.ts +48 -0
- package/dist/runtime/debug-controller.d.ts +7 -22
- package/dist/runtime/durable-execution.d.ts +176 -0
- package/dist/runtime/events.d.ts +16 -1
- package/dist/runtime/executable-module-contract.d.ts +16 -0
- package/dist/runtime/index.d.ts +13 -10
- package/dist/runtime/index.js +947 -9
- package/dist/runtime/strict-json.d.ts +15 -0
- package/dist/server/access.d.ts +12 -0
- package/dist/server/api.d.ts +126 -0
- package/dist/server/callback-delivery.d.ts +62 -0
- package/dist/server/callback-url.d.ts +44 -0
- package/dist/server/context.d.ts +35 -0
- package/dist/server/http-error.d.ts +10 -0
- package/dist/server/index.d.ts +7 -1
- package/dist/server/index.js +125 -6
- package/dist/server/openapi-document.d.ts +33 -0
- package/dist/server/openapi.d.ts +32 -0
- package/dist/server/respond.d.ts +35 -0
- package/dist/server/route-params.d.ts +17 -0
- package/dist/server/route-plan.d.ts +41 -0
- package/dist/server/routes-catalog.d.ts +5 -0
- package/dist/server/routes-declared.d.ts +12 -0
- package/dist/server/routes-runs.d.ts +8 -0
- package/dist/server/run-driver.d.ts +58 -0
- package/dist/server/run-view.d.ts +26 -0
- package/dist/server/transport.d.ts +35 -0
- package/dist/server/types.d.ts +143 -52
- package/dist/server/webhook-server.d.ts +12 -38
- package/dist/server/workflow-registry.d.ts +7 -4
- package/dist/service-registry.d.ts +49 -0
- package/dist/service-registry.js +123 -0
- package/dist/testing/assertions.d.ts +2 -2
- package/dist/testing/index.d.ts +14 -13
- package/dist/testing/index.js +582 -24
- package/dist/testing/mock-approval.d.ts +3 -5
- package/dist/testing/mock-llm.d.ts +2 -3
- package/dist/testing/recorder.d.ts +5 -6
- package/dist/testing/replayer.d.ts +6 -7
- package/dist/testing/run-store-check.d.ts +3 -0
- package/dist/testing/token-tracker.d.ts +2 -2
- package/dist/{type-mappings.d.ts → types/type-mappings.d.ts} +13 -1
- package/dist/utils/error-utils.d.ts +4 -2
- package/dist/utils/port-ordering.d.ts +2 -2
- package/dist/utils/port-tag-utils.d.ts +1 -1
- package/dist/validation/agent-detection.d.ts +17 -5
- package/dist/validation/agent-rules.d.ts +6 -7
- package/dist/validation/core-rules.d.ts +49 -0
- package/dist/validation/design-rules.d.ts +9 -3
- package/dist/validation/rule-registry.d.ts +3 -3
- package/dist/validation/rules/connections.d.ts +27 -0
- package/dist/validation/rules/context.d.ts +11 -0
- package/dist/validation/rules/cycles.d.ts +17 -0
- package/dist/validation/rules/data-flow.d.ts +19 -0
- package/dist/validation/rules/scope-topology.d.ts +35 -0
- package/dist/validation/rules/type-compatibility.d.ts +30 -0
- package/dist/validation/type-checker.d.ts +39 -0
- package/dist/validation/validator-helpers.d.ts +31 -0
- package/dist/validation/validator.d.ts +30 -0
- package/docs/reference/advanced-annotations.md +277 -60
- package/docs/reference/built-in-nodes.md +164 -94
- package/docs/reference/cancellation.md +44 -0
- package/docs/reference/cli-reference.md +252 -234
- package/docs/reference/compilation.md +36 -109
- package/docs/reference/concepts.md +80 -63
- package/docs/reference/console.md +169 -0
- package/docs/reference/debugging.md +26 -57
- package/docs/reference/deployment.md +217 -143
- package/docs/reference/durable-gates.md +445 -0
- package/docs/reference/error-codes.md +90 -43
- package/docs/reference/export-interface.md +91 -26
- package/docs/reference/iterative-development.md +50 -28
- package/docs/reference/jsdoc-grammar.md +66 -311
- package/docs/reference/library.md +250 -0
- package/docs/reference/marketplace.md +301 -152
- package/docs/reference/mcp-tools.md +102 -0
- package/docs/reference/node-conversion.md +84 -59
- package/docs/reference/orientation.md +68 -0
- package/docs/reference/scaffold.md +5 -4
- package/docs/reference/tutorial.md +106 -166
- package/docs/reference/visual-reference.md +1 -1
- package/package.json +85 -50
- package/dist/agent/agent-loop.js +0 -156
- package/dist/agent/cli-spawn-config.js +0 -65
- package/dist/agent/device-connection.d.ts +0 -79
- package/dist/agent/device-connection.js +0 -184
- package/dist/agent/env-allowlist.js +0 -60
- package/dist/agent/providers/anthropic.js +0 -198
- package/dist/agent/providers/claude-cli.js +0 -161
- package/dist/agent/providers/openai-compat.js +0 -205
- package/dist/agent/providers/platform.d.ts +0 -23
- package/dist/agent/providers/platform.js +0 -132
- package/dist/agent/streaming.js +0 -186
- package/dist/agent/types.js +0 -20
- package/dist/annotation-generator.d.ts +0 -50
- package/dist/annotation-generator.js +0 -743
- package/dist/api/builder.js +0 -345
- package/dist/api/command-runner.js +0 -787
- package/dist/api/compile.js +0 -148
- package/dist/api/extract-types.js +0 -57
- package/dist/api/generate-in-place.js +0 -1477
- package/dist/api/generate.js +0 -514
- package/dist/api/helpers.js +0 -285
- package/dist/api/inline-runtime.js +0 -526
- package/dist/api/manipulation/connections.js +0 -151
- package/dist/api/manipulation/index.js +0 -41
- package/dist/api/manipulation/node-types.js +0 -200
- package/dist/api/manipulation/nodes.js +0 -355
- package/dist/api/manipulation/ports.js +0 -228
- package/dist/api/manipulation/scopes.js +0 -156
- package/dist/api/manipulation/workflow.js +0 -116
- package/dist/api/modify-operation.js +0 -177
- package/dist/api/parse.js +0 -115
- package/dist/api/patterns.d.ts +0 -112
- package/dist/api/patterns.js +0 -303
- package/dist/api/query.js +0 -840
- package/dist/api/templates.js +0 -117
- package/dist/api/transform.js +0 -40
- package/dist/api/validate.js +0 -64
- package/dist/api/validation-registry.js +0 -10
- package/dist/api/workflow-file-operations.js +0 -180
- package/dist/ast/builder.js +0 -395
- package/dist/ast/serialization-node.js +0 -30
- package/dist/ast/serialization.js +0 -134
- package/dist/ast/types.js +0 -2
- package/dist/ast/workflow-utils.js +0 -114
- package/dist/body-generator.js +0 -35
- package/dist/built-in-nodes/coercion-types.js +0 -61
- package/dist/built-in-nodes/delay.js +0 -29
- package/dist/built-in-nodes/generated-registry.js +0 -299
- package/dist/built-in-nodes/invoke-workflow.js +0 -36
- package/dist/built-in-nodes/mock-types.js +0 -42
- package/dist/built-in-nodes/wait-for-agent.js +0 -31
- package/dist/built-in-nodes/wait-for-event.js +0 -25
- package/dist/chevrotain-parser/coerce-parser.js +0 -103
- package/dist/chevrotain-parser/connect-parser.js +0 -136
- package/dist/chevrotain-parser/fan-parser.js +0 -149
- package/dist/chevrotain-parser/grammar-diagrams.js +0 -273
- package/dist/chevrotain-parser/index.js +0 -31
- package/dist/chevrotain-parser/map-parser.js +0 -130
- package/dist/chevrotain-parser/node-parser.js +0 -558
- package/dist/chevrotain-parser/path-parser.js +0 -140
- package/dist/chevrotain-parser/port-parser.js +0 -519
- package/dist/chevrotain-parser/position-parser.d.ts +0 -20
- package/dist/chevrotain-parser/position-parser.js +0 -83
- package/dist/chevrotain-parser/scope-parser.js +0 -104
- package/dist/chevrotain-parser/tokens.js +0 -429
- package/dist/chevrotain-parser/trigger-cancel-parser.js +0 -291
- package/dist/cli/commands/account.d.ts +0 -4
- package/dist/cli/commands/account.js +0 -50
- package/dist/cli/commands/ai-credentials.d.ts +0 -12
- package/dist/cli/commands/ai-credentials.js +0 -95
- package/dist/cli/commands/apikey.d.ts +0 -4
- package/dist/cli/commands/apikey.js +0 -64
- package/dist/cli/commands/auth.d.ts +0 -9
- package/dist/cli/commands/auth.js +0 -255
- package/dist/cli/commands/compile.js +0 -331
- package/dist/cli/commands/connect.d.ts +0 -4
- package/dist/cli/commands/connect.js +0 -163
- package/dist/cli/commands/context.js +0 -51
- package/dist/cli/commands/create.js +0 -139
- package/dist/cli/commands/deploy.d.ts +0 -6
- package/dist/cli/commands/deploy.js +0 -74
- package/dist/cli/commands/dev.js +0 -227
- package/dist/cli/commands/diagram.js +0 -39
- package/dist/cli/commands/diff.js +0 -57
- package/dist/cli/commands/docs.js +0 -74
- package/dist/cli/commands/export.js +0 -153
- package/dist/cli/commands/grammar.js +0 -40
- package/dist/cli/commands/implement.js +0 -91
- package/dist/cli/commands/init-personas.js +0 -541
- package/dist/cli/commands/init.js +0 -797
- package/dist/cli/commands/market.js +0 -440
- package/dist/cli/commands/mcp-setup.js +0 -433
- package/dist/cli/commands/migrate.js +0 -91
- package/dist/cli/commands/modify.js +0 -58
- package/dist/cli/commands/openapi.js +0 -71
- package/dist/cli/commands/org.d.ts +0 -8
- package/dist/cli/commands/org.js +0 -136
- package/dist/cli/commands/pattern.d.ts +0 -34
- package/dist/cli/commands/pattern.js +0 -186
- package/dist/cli/commands/plugin.d.ts +0 -16
- package/dist/cli/commands/plugin.js +0 -176
- package/dist/cli/commands/run.js +0 -742
- package/dist/cli/commands/serve.js +0 -93
- package/dist/cli/commands/status.js +0 -123
- package/dist/cli/commands/strip.js +0 -61
- package/dist/cli/commands/templates.js +0 -58
- package/dist/cli/commands/validate.js +0 -267
- package/dist/cli/commands/watch.js +0 -71
- package/dist/cli/config/credentials.d.ts +0 -15
- package/dist/cli/config/credentials.js +0 -43
- package/dist/cli/config/platform-client.d.ts +0 -123
- package/dist/cli/config/platform-client.js +0 -248
- package/dist/cli/env-setup.js +0 -12
- package/dist/cli/index.js +0 -850
- package/dist/cli/pack-commands.js +0 -122
- package/dist/cli/postinstall.d.ts +0 -16
- package/dist/cli/postinstall.js +0 -119
- package/dist/cli/templates/ai/mock-provider.js +0 -64
- package/dist/cli/templates/ai/types.js +0 -5
- package/dist/cli/templates/approvals/index.d.ts +0 -15
- package/dist/cli/templates/approvals/index.js +0 -241
- package/dist/cli/templates/index.js +0 -123
- package/dist/cli/templates/nodes/agent-router.js +0 -114
- package/dist/cli/templates/nodes/aggregator.js +0 -63
- package/dist/cli/templates/nodes/conversation-memory.js +0 -85
- package/dist/cli/templates/nodes/http.js +0 -80
- package/dist/cli/templates/nodes/human-approval.js +0 -109
- package/dist/cli/templates/nodes/json-extractor.js +0 -119
- package/dist/cli/templates/nodes/llm-call.js +0 -106
- package/dist/cli/templates/nodes/prompt-template.js +0 -52
- package/dist/cli/templates/nodes/rag-retriever.js +0 -128
- package/dist/cli/templates/nodes/tool-executor.js +0 -108
- package/dist/cli/templates/nodes/transformer.js +0 -68
- package/dist/cli/templates/nodes/validator.js +0 -62
- package/dist/cli/templates/pack-loader.js +0 -58
- package/dist/cli/templates/providers/index.js +0 -239
- package/dist/cli/templates/shared/approval-types.js +0 -31
- package/dist/cli/templates/shared/llm-types.js +0 -109
- package/dist/cli/templates/workflows/aggregator.js +0 -101
- package/dist/cli/templates/workflows/ai-agent.js +0 -326
- package/dist/cli/templates/workflows/ai-chat.js +0 -167
- package/dist/cli/templates/workflows/ai-rag.js +0 -184
- package/dist/cli/templates/workflows/ai-react.js +0 -293
- package/dist/cli/templates/workflows/conditional.js +0 -139
- package/dist/cli/templates/workflows/error-handler.js +0 -145
- package/dist/cli/templates/workflows/foreach.js +0 -140
- package/dist/cli/templates/workflows/sequential.js +0 -200
- package/dist/cli/templates/workflows/webhook.js +0 -158
- package/dist/cli/utils/cli-helpers.d.ts +0 -44
- package/dist/cli/utils/cli-helpers.js +0 -99
- package/dist/cli/utils/logger.js +0 -126
- package/dist/cli/utils/parse-int-strict.js +0 -17
- package/dist/cli/utils/safe-write.js +0 -54
- package/dist/deployment/config/defaults.d.ts +0 -29
- package/dist/deployment/config/defaults.js +0 -98
- package/dist/deployment/config/loader.d.ts +0 -24
- package/dist/deployment/config/loader.js +0 -237
- package/dist/deployment/config/types.d.ts +0 -117
- package/dist/deployment/config/types.js +0 -5
- package/dist/deployment/core/adapters.d.ts +0 -66
- package/dist/deployment/core/adapters.js +0 -156
- package/dist/deployment/core/executor.d.ts +0 -62
- package/dist/deployment/core/executor.js +0 -197
- package/dist/deployment/core/formatters.d.ts +0 -42
- package/dist/deployment/core/formatters.js +0 -146
- package/dist/deployment/openapi/generator.d.ts +0 -146
- package/dist/deployment/openapi/generator.js +0 -347
- package/dist/deployment/openapi/schema-converter.d.ts +0 -49
- package/dist/deployment/openapi/schema-converter.js +0 -192
- package/dist/deployment/targets/base.js +0 -855
- package/dist/deployment/types.d.ts +0 -140
- package/dist/deployment/types.js +0 -8
- package/dist/diagram/ascii-renderer.js +0 -528
- package/dist/diagram/geometry.js +0 -1267
- package/dist/diagram/html-viewer.d.ts +0 -21
- package/dist/diagram/html-viewer.js +0 -1689
- package/dist/diagram/layout.js +0 -143
- package/dist/diagram/orthogonal-router.js +0 -568
- package/dist/diagram/renderer.d.ts +0 -3
- package/dist/diagram/renderer.js +0 -266
- package/dist/diagram/theme.js +0 -282
- package/dist/diagram/types.js +0 -2
- package/dist/diff/WorkflowDiffer.js +0 -429
- package/dist/diff/formatDiff.js +0 -239
- package/dist/diff/impact.js +0 -119
- package/dist/diff/types.js +0 -35
- package/dist/doc-metadata/extractors/annotations.js +0 -638
- package/dist/doc-metadata/extractors/cli-commands.js +0 -326
- package/dist/doc-metadata/extractors/core-metadata.js +0 -61
- package/dist/doc-metadata/extractors/error-codes.js +0 -372
- package/dist/doc-metadata/extractors/grammar-rules.js +0 -51
- package/dist/doc-metadata/extractors/mcp-tools.js +0 -705
- package/dist/doc-metadata/extractors/plugin-api.d.ts +0 -19
- package/dist/doc-metadata/extractors/plugin-api.js +0 -279
- package/dist/doc-metadata/types.js +0 -5
- package/dist/editor-completions/annotationValues.js +0 -146
- package/dist/editor-completions/contextParser.js +0 -410
- package/dist/editor-completions/dataTypes.js +0 -95
- package/dist/editor-completions/goToDefinition.js +0 -112
- package/dist/editor-completions/jsDocAnnotations.js +0 -359
- package/dist/editor-completions/modifierCompletions.js +0 -197
- package/dist/editor-completions/types.js +0 -8
- package/dist/export/index.js +0 -212
- package/dist/extensions/index.d.ts +0 -16
- package/dist/extensions/index.js +0 -17
- package/dist/friendly-errors.js +0 -761
- package/dist/function-like.js +0 -84
- package/dist/generator/async-detection.js +0 -56
- package/dist/generator/code-utils.js +0 -551
- package/dist/generator/compile-target-registry.d.ts +0 -20
- package/dist/generator/compile-target-registry.js +0 -20
- package/dist/generator/control-flow.js +0 -358
- package/dist/generator/dev-mode-registry.d.ts +0 -27
- package/dist/generator/dev-mode-registry.js +0 -20
- package/dist/generator/scope-function-generator.js +0 -397
- package/dist/generator/unified.js +0 -1831
- package/dist/generator.d.ts +0 -54
- package/dist/generator.js +0 -101
- package/dist/jsdoc-parser.d.ts +0 -377
- package/dist/jsdoc-parser.js +0 -1189
- package/dist/jsdoc-port-sync/constants.js +0 -103
- package/dist/jsdoc-port-sync/diff.js +0 -319
- package/dist/jsdoc-port-sync/port-parser.js +0 -579
- package/dist/jsdoc-port-sync/rename.js +0 -256
- package/dist/jsdoc-port-sync/signature-parser.js +0 -559
- package/dist/jsdoc-port-sync/sync.js +0 -653
- package/dist/marketplace/manifest.js +0 -194
- package/dist/marketplace/registry.js +0 -198
- package/dist/marketplace/types.js +0 -9
- package/dist/marketplace/validator.js +0 -150
- package/dist/mcp/agent-channel.d.ts +0 -35
- package/dist/mcp/agent-channel.js +0 -61
- package/dist/mcp/auto-registration.d.ts +0 -3
- package/dist/mcp/auto-registration.js +0 -62
- package/dist/mcp/debug-session.js +0 -25
- package/dist/mcp/index.d.ts +0 -11
- package/dist/mcp/index.js +0 -10
- package/dist/mcp/pack-tools.js +0 -59
- package/dist/mcp/prompts.js +0 -68
- package/dist/mcp/response-utils.js +0 -97
- package/dist/mcp/run-registry.d.ts +0 -29
- package/dist/mcp/run-registry.js +0 -24
- package/dist/mcp/server.js +0 -63
- package/dist/mcp/tools-context.js +0 -51
- package/dist/mcp/tools-debug.js +0 -451
- package/dist/mcp/tools-diagram.js +0 -97
- package/dist/mcp/tools-docs.js +0 -62
- package/dist/mcp/tools-export.js +0 -222
- package/dist/mcp/tools-marketplace.js +0 -132
- package/dist/mcp/tools-model.d.ts +0 -3
- package/dist/mcp/tools-model.js +0 -279
- package/dist/mcp/tools-pattern.d.ts +0 -3
- package/dist/mcp/tools-pattern.js +0 -604
- package/dist/mcp/tools-query.js +0 -377
- package/dist/mcp/tools-resources.js +0 -41
- package/dist/mcp/tools-template.js +0 -119
- package/dist/mcp/tools-workflow-run.d.ts +0 -40
- package/dist/mcp/tools-workflow-run.js +0 -150
- package/dist/mcp/types.js +0 -2
- package/dist/mcp/workflow-executor.js +0 -242
- package/dist/migration/registry.js +0 -29
- package/dist/node-types-generator.js +0 -139
- package/dist/parser/tag-registry.js +0 -57
- package/dist/parser.d.ts +0 -232
- package/dist/parser.js +0 -2500
- package/dist/plugin/PluginPanel.d.ts +0 -12
- package/dist/plugin/PluginPanel.js +0 -5
- package/dist/plugin/index.d.ts +0 -13
- package/dist/plugin/index.js +0 -14
- package/dist/plugin/types.d.ts +0 -75
- package/dist/plugin/types.js +0 -8
- package/dist/resolve-package-types.js +0 -123
- package/dist/runtime/CancellationError.js +0 -20
- package/dist/runtime/ExecutionContext.js +0 -280
- package/dist/runtime/builtin-functions.js +0 -549
- package/dist/runtime/checkpoint.d.ts +0 -84
- package/dist/runtime/checkpoint.js +0 -225
- package/dist/runtime/debug-controller.js +0 -247
- package/dist/runtime/events.js +0 -2
- package/dist/runtime/function-registry.js +0 -66
- package/dist/runtime/parameter-resolver.js +0 -113
- package/dist/server/types.js +0 -5
- package/dist/server/webhook-server.js +0 -269
- package/dist/server/workflow-registry.js +0 -202
- package/dist/shared-project.js +0 -28
- package/dist/sugar-optimizer.js +0 -498
- package/dist/testing/assertions.js +0 -127
- package/dist/testing/mock-approval.js +0 -98
- package/dist/testing/mock-llm.js +0 -119
- package/dist/testing/recorder.js +0 -70
- package/dist/testing/replayer.js +0 -143
- package/dist/testing/token-tracker.js +0 -94
- package/dist/type-checker.d.ts +0 -42
- package/dist/type-checker.js +0 -190
- package/dist/type-mappings.js +0 -131
- package/dist/types/branded-ports.d.ts +0 -151
- package/dist/types/branded-ports.js +0 -121
- package/dist/types/index.d.ts +0 -5
- package/dist/types/index.js +0 -5
- package/dist/types.d.ts +0 -139
- package/dist/types.js +0 -15
- package/dist/utils/error-utils.js +0 -27
- package/dist/utils/lru-cache.js +0 -40
- package/dist/utils/port-ordering.js +0 -81
- package/dist/utils/port-tag-utils.js +0 -41
- package/dist/utils/string-distance.js +0 -56
- package/dist/validation/agent-detection.js +0 -115
- package/dist/validation/agent-rules.js +0 -262
- package/dist/validation/design-rules.js +0 -377
- package/dist/validation/rule-registry.js +0 -37
- package/dist/validator.d.ts +0 -113
- package/dist/validator.js +0 -1631
- package/docs/reference/patterns.md +0 -159
- package/scripts/postinstall.cjs +0 -86
- /package/dist/{shared-project.d.ts → parser/shared-project.d.ts} +0 -0
- /package/dist/{friendly-errors.d.ts → validation/friendly-errors.d.ts} +0 -0
|
@@ -0,0 +1,2925 @@
|
|
|
1
|
+
import { createRequire as __createRequire } from "module";
|
|
2
|
+
const require = __createRequire(import.meta.url);
|
|
3
|
+
import {
|
|
4
|
+
exportWorkflow
|
|
5
|
+
} from "./flow-weaver-chunk-54M4KNEL.mjs";
|
|
6
|
+
import {
|
|
7
|
+
ERROR_HINTS
|
|
8
|
+
} from "./flow-weaver-chunk-KOEMJHLG.mjs";
|
|
9
|
+
import {
|
|
10
|
+
loadPackDocTopics
|
|
11
|
+
} from "./flow-weaver-chunk-J7EPZW2I.mjs";
|
|
12
|
+
import {
|
|
13
|
+
DebugController
|
|
14
|
+
} from "./flow-weaver-chunk-FPPPU2DG.mjs";
|
|
15
|
+
import {
|
|
16
|
+
ARTIFACT_KINDS,
|
|
17
|
+
renderArtifact
|
|
18
|
+
} from "./flow-weaver-chunk-Z5ZJPNSY.mjs";
|
|
19
|
+
import {
|
|
20
|
+
WorkflowDiffer,
|
|
21
|
+
getImpactReasons
|
|
22
|
+
} from "./flow-weaver-chunk-7ET6N32V.mjs";
|
|
23
|
+
import {
|
|
24
|
+
buildProcessModel,
|
|
25
|
+
stepLabel
|
|
26
|
+
} from "./flow-weaver-chunk-HMAKDE55.mjs";
|
|
27
|
+
import {
|
|
28
|
+
getPackDocTopics,
|
|
29
|
+
listTopics,
|
|
30
|
+
readTopic,
|
|
31
|
+
readTopicStructured,
|
|
32
|
+
searchDocs
|
|
33
|
+
} from "./flow-weaver-chunk-XG2WKXYZ.mjs";
|
|
34
|
+
import {
|
|
35
|
+
generateManifest,
|
|
36
|
+
readManifest
|
|
37
|
+
} from "./flow-weaver-chunk-AA3NATMX.mjs";
|
|
38
|
+
import {
|
|
39
|
+
validatePackage
|
|
40
|
+
} from "./flow-weaver-chunk-2C7PZCV5.mjs";
|
|
41
|
+
import {
|
|
42
|
+
RESERVED_PATHS,
|
|
43
|
+
gateOutputSchemas,
|
|
44
|
+
isLoopback,
|
|
45
|
+
planRoutes,
|
|
46
|
+
workflowParamsSchema
|
|
47
|
+
} from "./flow-weaver-chunk-MHJ4N652.mjs";
|
|
48
|
+
import {
|
|
49
|
+
answerAgentGate,
|
|
50
|
+
computeBundleDigest,
|
|
51
|
+
createFileEffectAdapter,
|
|
52
|
+
createLocalCoordinator,
|
|
53
|
+
defaultRunsDir,
|
|
54
|
+
isAnswering,
|
|
55
|
+
noteAnswerMisfit,
|
|
56
|
+
reclaimStaleAgentAnswers,
|
|
57
|
+
transcriptName,
|
|
58
|
+
tryProfile
|
|
59
|
+
} from "./flow-weaver-chunk-YLSRZWFK.mjs";
|
|
60
|
+
import "./flow-weaver-chunk-EKWF27G6.mjs";
|
|
61
|
+
import {
|
|
62
|
+
DEFAULT_MODEL,
|
|
63
|
+
STARTER_AGENTS_YAML,
|
|
64
|
+
SUGGESTED_MODELS,
|
|
65
|
+
agentsFile,
|
|
66
|
+
keyEnvOf,
|
|
67
|
+
loadAgentProfiles,
|
|
68
|
+
readiness,
|
|
69
|
+
saveAgentProfiles,
|
|
70
|
+
validateProfile
|
|
71
|
+
} from "./flow-weaver-chunk-TS5P3PIN.mjs";
|
|
72
|
+
import {
|
|
73
|
+
executeWorkflow
|
|
74
|
+
} from "./flow-weaver-chunk-GBUNNP33.mjs";
|
|
75
|
+
import {
|
|
76
|
+
getAvailableWorkflows
|
|
77
|
+
} from "./flow-weaver-chunk-NQKKWDGF.mjs";
|
|
78
|
+
import "./flow-weaver-chunk-V6DHWFQG.mjs";
|
|
79
|
+
import {
|
|
80
|
+
createTargetRegistry
|
|
81
|
+
} from "./flow-weaver-chunk-3MKUQP3D.mjs";
|
|
82
|
+
import {
|
|
83
|
+
runDoctorChecks
|
|
84
|
+
} from "./flow-weaver-chunk-U3EO3X4B.mjs";
|
|
85
|
+
import {
|
|
86
|
+
installRoot,
|
|
87
|
+
isAlive,
|
|
88
|
+
listServices
|
|
89
|
+
} from "./flow-weaver-chunk-MSI2UJES.mjs";
|
|
90
|
+
import "./flow-weaver-chunk-3BSYCWPL.mjs";
|
|
91
|
+
import {
|
|
92
|
+
listInstalledPackages,
|
|
93
|
+
resolveRegistries,
|
|
94
|
+
searchAllRegistries
|
|
95
|
+
} from "./flow-weaver-chunk-4DVY73ML.mjs";
|
|
96
|
+
import {
|
|
97
|
+
validateWorkflow
|
|
98
|
+
} from "./flow-weaver-chunk-ZQALO25T.mjs";
|
|
99
|
+
import "./flow-weaver-chunk-DM4BBRRU.mjs";
|
|
100
|
+
import "./flow-weaver-chunk-6ZUJ7LMX.mjs";
|
|
101
|
+
import "./flow-weaver-chunk-A6M5K3W7.mjs";
|
|
102
|
+
import {
|
|
103
|
+
parseWorkflow
|
|
104
|
+
} from "./flow-weaver-chunk-W3YNK26P.mjs";
|
|
105
|
+
import "./flow-weaver-chunk-BHCC5FE5.mjs";
|
|
106
|
+
import {
|
|
107
|
+
VERSION
|
|
108
|
+
} from "./flow-weaver-chunk-VXWNLYQU.mjs";
|
|
109
|
+
import "./flow-weaver-chunk-RIWE7YTR.mjs";
|
|
110
|
+
import "./flow-weaver-chunk-IJJ4JXTL.mjs";
|
|
111
|
+
import "./flow-weaver-chunk-55QMUZIA.mjs";
|
|
112
|
+
import "./flow-weaver-chunk-EM5HWQ4R.mjs";
|
|
113
|
+
import {
|
|
114
|
+
getTopologicalOrder
|
|
115
|
+
} from "./flow-weaver-chunk-Z44JK332.mjs";
|
|
116
|
+
import {
|
|
117
|
+
hasInPlaceMarkers,
|
|
118
|
+
httpRouteText,
|
|
119
|
+
isConnectionCoveredByMacroStatic
|
|
120
|
+
} from "./flow-weaver-chunk-APX6MQ5A.mjs";
|
|
121
|
+
import "./flow-weaver-chunk-7G63RR4K.mjs";
|
|
122
|
+
import {
|
|
123
|
+
getErrorMessage
|
|
124
|
+
} from "./flow-weaver-chunk-OMVWSNHJ.mjs";
|
|
125
|
+
import "./flow-weaver-chunk-GAQXZHNO.mjs";
|
|
126
|
+
import "./flow-weaver-chunk-DRN7KPWY.mjs";
|
|
127
|
+
import "./flow-weaver-chunk-N767YRAI.mjs";
|
|
128
|
+
|
|
129
|
+
// src/console/server.ts
|
|
130
|
+
import * as http from "node:http";
|
|
131
|
+
import * as fs13 from "node:fs";
|
|
132
|
+
import * as path13 from "node:path";
|
|
133
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
134
|
+
|
|
135
|
+
// src/console/services.ts
|
|
136
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
137
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
138
|
+
import * as fs2 from "node:fs";
|
|
139
|
+
import * as os from "node:os";
|
|
140
|
+
import * as path2 from "node:path";
|
|
141
|
+
|
|
142
|
+
// src/console/cli-run.ts
|
|
143
|
+
import { spawn } from "node:child_process";
|
|
144
|
+
import * as fs from "node:fs";
|
|
145
|
+
import * as path from "node:path";
|
|
146
|
+
import { fileURLToPath } from "node:url";
|
|
147
|
+
var NOT_FROM_CONSOLE = /* @__PURE__ */ new Set([
|
|
148
|
+
"console",
|
|
149
|
+
"mcp-server",
|
|
150
|
+
"mcp-setup",
|
|
151
|
+
"dev",
|
|
152
|
+
"watch",
|
|
153
|
+
"serve"
|
|
154
|
+
]);
|
|
155
|
+
function planFwCommand(argv) {
|
|
156
|
+
const args = argv[0] === "fw" || argv[0] === "flow-weaver" ? argv.slice(1) : [...argv];
|
|
157
|
+
if (!args.length) return { ok: false, error: "nothing to run" };
|
|
158
|
+
if (args.some((a) => typeof a !== "string")) return { ok: false, error: "arguments must be strings" };
|
|
159
|
+
const command = args[0];
|
|
160
|
+
if (NOT_FROM_CONSOLE.has(command)) {
|
|
161
|
+
return { ok: false, error: `\`fw ${command}\` runs outside the console: it is long-lived. Use a terminal.` };
|
|
162
|
+
}
|
|
163
|
+
return { ok: true, args };
|
|
164
|
+
}
|
|
165
|
+
function resolveCliEntry(here = path.dirname(fileURLToPath(import.meta.url))) {
|
|
166
|
+
const built = path.join(here, "..", "cli", "flow-weaver.mjs");
|
|
167
|
+
if (fs.existsSync(built)) return { exec: process.execPath, prefix: [built] };
|
|
168
|
+
const source = path.join(here, "..", "cli", "index.ts");
|
|
169
|
+
if (fs.existsSync(source)) return { exec: process.execPath, prefix: ["--import", "tsx", source] };
|
|
170
|
+
throw new Error("cannot find the fw CLI beside the console");
|
|
171
|
+
}
|
|
172
|
+
function spawnFw(args, cwd) {
|
|
173
|
+
const { exec, prefix } = resolveCliEntry();
|
|
174
|
+
const { NODE_OPTIONS: _flags, ...env } = process.env;
|
|
175
|
+
return spawn(exec, [...prefix, ...args], {
|
|
176
|
+
cwd,
|
|
177
|
+
env: { ...env, FORCE_COLOR: "0", NO_COLOR: "1" },
|
|
178
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
179
|
+
windowsHide: true
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// src/console/services.ts
|
|
184
|
+
var DEFAULT_SETTINGS = {
|
|
185
|
+
serve: { port: 3e3, host: "127.0.0.1", auth: "token", agents: true, trace: true, dev: false, swagger: false, autoStart: false },
|
|
186
|
+
watch: { autoStart: false }
|
|
187
|
+
};
|
|
188
|
+
var MAX_LINES = 500;
|
|
189
|
+
var STOP_GRACE_MS = 5e3;
|
|
190
|
+
function consoleSettingsDir() {
|
|
191
|
+
return process.env.FW_CONSOLE_DIR ?? path2.join(os.homedir(), ".fw", "console");
|
|
192
|
+
}
|
|
193
|
+
function spawnFwWith(args, cwd, env) {
|
|
194
|
+
const { exec, prefix } = resolveCliEntry();
|
|
195
|
+
return spawn2(exec, [...prefix, ...args], { cwd, env, stdio: ["ignore", "pipe", "pipe"], windowsHide: true });
|
|
196
|
+
}
|
|
197
|
+
var Supervisor = class {
|
|
198
|
+
constructor(opts) {
|
|
199
|
+
this.opts = opts;
|
|
200
|
+
this.projectDir = path2.resolve(opts.projectDir);
|
|
201
|
+
const dir = opts.settingsDir ?? consoleSettingsDir();
|
|
202
|
+
this.file = path2.join(dir, `${createHash("sha256").update(real(this.projectDir)).digest("hex").slice(0, 16)}.json`);
|
|
203
|
+
}
|
|
204
|
+
opts;
|
|
205
|
+
children = /* @__PURE__ */ new Map();
|
|
206
|
+
listeners = /* @__PURE__ */ new Map();
|
|
207
|
+
file;
|
|
208
|
+
cache;
|
|
209
|
+
projectDir;
|
|
210
|
+
// ---------------------------------------------------------- settings
|
|
211
|
+
settings() {
|
|
212
|
+
if (this.cache) return this.cache;
|
|
213
|
+
let stored;
|
|
214
|
+
try {
|
|
215
|
+
stored = JSON.parse(fs2.readFileSync(this.file, "utf8"));
|
|
216
|
+
} catch {
|
|
217
|
+
stored = {};
|
|
218
|
+
}
|
|
219
|
+
this.cache = {
|
|
220
|
+
serve: { ...DEFAULT_SETTINGS.serve, ...stored.serve ?? {} },
|
|
221
|
+
watch: { ...DEFAULT_SETTINGS.watch, ...stored.watch ?? {} }
|
|
222
|
+
};
|
|
223
|
+
return this.cache;
|
|
224
|
+
}
|
|
225
|
+
saveSettings(kind, patch) {
|
|
226
|
+
const current = this.settings();
|
|
227
|
+
const next = { ...current, [kind]: { ...current[kind], ...patch } };
|
|
228
|
+
if (kind === "serve") {
|
|
229
|
+
const s = next.serve;
|
|
230
|
+
if (!Number.isInteger(s.port) || s.port < 0 || s.port > 65535) throw new Error("port must be a whole number between 0 and 65535");
|
|
231
|
+
if (!/^[A-Za-z0-9.:[\]-]+$/.test(s.host)) throw new Error("host must be a host name or an address");
|
|
232
|
+
if (s.auth !== "token" && s.auth !== "open") throw new Error("auth is token or open");
|
|
233
|
+
if (s.auth === "open" && !["127.0.0.1", "localhost", "::1", "[::1]"].includes(s.host)) throw new Error("a server beyond loopback needs a token: set auth to token, or host to 127.0.0.1");
|
|
234
|
+
}
|
|
235
|
+
fs2.mkdirSync(path2.dirname(this.file), { recursive: true });
|
|
236
|
+
const tmp = `${this.file}.${process.pid}.tmp`;
|
|
237
|
+
fs2.writeFileSync(tmp, JSON.stringify(next, null, 2));
|
|
238
|
+
fs2.renameSync(tmp, this.file);
|
|
239
|
+
this.cache = next;
|
|
240
|
+
return next;
|
|
241
|
+
}
|
|
242
|
+
// ------------------------------------------------------------ control
|
|
243
|
+
/** Start a service with the saved settings. A running one is left alone. */
|
|
244
|
+
start(kind) {
|
|
245
|
+
const existing = this.children.get(kind);
|
|
246
|
+
if (existing && (existing.state === "running" || existing.state === "starting")) return this.view(kind);
|
|
247
|
+
const others = this.registryFor(kind);
|
|
248
|
+
if (kind === "serve" && others.length) throw new Error(`fw serve is already running for this project (pid ${others[0].pid}${others[0].url ? `, ${others[0].url}` : ""}). Stop it first`);
|
|
249
|
+
const settings = this.settings();
|
|
250
|
+
const { NODE_OPTIONS: _flags, ...base } = process.env;
|
|
251
|
+
const env = { ...base, FORCE_COLOR: "0", NO_COLOR: "1" };
|
|
252
|
+
let args;
|
|
253
|
+
let token;
|
|
254
|
+
if (kind === "serve") {
|
|
255
|
+
const s = settings.serve;
|
|
256
|
+
args = ["serve", this.projectDir, "--port", String(s.port), "--host", s.host];
|
|
257
|
+
if (s.auth === "token") {
|
|
258
|
+
token = randomBytes(24).toString("hex");
|
|
259
|
+
env.FW_SERVE_TOKEN = token;
|
|
260
|
+
} else {
|
|
261
|
+
delete env.FW_SERVE_TOKEN;
|
|
262
|
+
if (!["127.0.0.1", "localhost", "::1", "[::1]"].includes(s.host)) args.push("--insecure");
|
|
263
|
+
}
|
|
264
|
+
if (!s.agents) args.push("--no-agents");
|
|
265
|
+
if (s.trace) args.push("--trace");
|
|
266
|
+
if (s.dev) args.push("--dev");
|
|
267
|
+
if (s.swagger) args.push("--swagger");
|
|
268
|
+
} else {
|
|
269
|
+
args = ["watch", this.projectDir];
|
|
270
|
+
}
|
|
271
|
+
const proc = (this.opts.spawn ?? spawnFwWith)(args, this.projectDir, env);
|
|
272
|
+
const child = { kind, proc, state: "starting", startedAt: (/* @__PURE__ */ new Date()).toISOString(), token, lines: [] };
|
|
273
|
+
this.children.set(kind, child);
|
|
274
|
+
if (kind === "watch") child.state = "running";
|
|
275
|
+
const onData = (stream) => (chunk) => {
|
|
276
|
+
for (const raw of chunk.toString().split(/\r?\n/)) {
|
|
277
|
+
if (!raw.trim()) continue;
|
|
278
|
+
const line = { t: Date.now(), stream, text: raw };
|
|
279
|
+
child.lines.push(line);
|
|
280
|
+
if (child.lines.length > MAX_LINES) child.lines.splice(0, child.lines.length - MAX_LINES);
|
|
281
|
+
const listening = /Listening:\s*(https?:\/\/\S+)/.exec(raw);
|
|
282
|
+
if (listening && child.state === "starting") {
|
|
283
|
+
child.state = "running";
|
|
284
|
+
child.url = listening[1];
|
|
285
|
+
this.opts.onChange?.({ kind, state: "running", url: child.url });
|
|
286
|
+
}
|
|
287
|
+
for (const fn of this.listeners.get(kind) ?? []) fn(line);
|
|
288
|
+
this.opts.onChange?.({ kind, state: child.state, line });
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
proc.stdout?.on("data", onData("out"));
|
|
292
|
+
proc.stderr?.on("data", onData("err"));
|
|
293
|
+
proc.on("error", (err) => {
|
|
294
|
+
child.state = "exited";
|
|
295
|
+
child.error = err.message;
|
|
296
|
+
this.opts.onChange?.({ kind, state: "exited", error: err.message });
|
|
297
|
+
});
|
|
298
|
+
proc.on("close", (code) => {
|
|
299
|
+
if (child.state === "exited") return;
|
|
300
|
+
child.state = "exited";
|
|
301
|
+
child.exitCode = code;
|
|
302
|
+
const lastErr = [...child.lines].reverse().find((l) => l.stream === "err")?.text;
|
|
303
|
+
if (code && code !== 0 && !child.stopping) child.error = lastErr ?? `exited with code ${code}`;
|
|
304
|
+
this.opts.onChange?.({ kind, state: "exited", exitCode: code, error: child.error });
|
|
305
|
+
});
|
|
306
|
+
this.opts.onChange?.({ kind, state: child.state });
|
|
307
|
+
return this.view(kind);
|
|
308
|
+
}
|
|
309
|
+
/** Stop our child of this kind, or another process of this kind for the project by pid. */
|
|
310
|
+
async stop(kind, pid) {
|
|
311
|
+
const child = this.children.get(kind);
|
|
312
|
+
if (child && (pid === void 0 || child.proc.pid === pid) && child.state !== "exited") {
|
|
313
|
+
child.stopping = true;
|
|
314
|
+
await terminate(child.proc);
|
|
315
|
+
return this.view(kind);
|
|
316
|
+
}
|
|
317
|
+
if (pid !== void 0) {
|
|
318
|
+
const other = this.registryFor(kind).find((r) => r.pid === pid);
|
|
319
|
+
if (!other) throw new Error(`no ${kind} with pid ${pid} for this project`);
|
|
320
|
+
try {
|
|
321
|
+
process.kill(pid, "SIGTERM");
|
|
322
|
+
} catch (e) {
|
|
323
|
+
throw new Error(`could not stop pid ${pid}: ${e.message}`, { cause: e });
|
|
324
|
+
}
|
|
325
|
+
const end = Date.now() + STOP_GRACE_MS;
|
|
326
|
+
while (Date.now() < end && isAlive(pid)) await sleep(100);
|
|
327
|
+
if (isAlive(pid)) {
|
|
328
|
+
try {
|
|
329
|
+
process.kill(pid, "SIGKILL");
|
|
330
|
+
} catch {
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return this.view(kind);
|
|
335
|
+
}
|
|
336
|
+
async restart(kind) {
|
|
337
|
+
await this.stop(kind);
|
|
338
|
+
return this.start(kind);
|
|
339
|
+
}
|
|
340
|
+
/** Stop every child; called when the console closes. */
|
|
341
|
+
async close() {
|
|
342
|
+
await Promise.all([...this.children.values()].filter((c) => c.state !== "exited").map((c) => {
|
|
343
|
+
c.stopping = true;
|
|
344
|
+
return terminate(c.proc);
|
|
345
|
+
}));
|
|
346
|
+
}
|
|
347
|
+
// -------------------------------------------------------------- reading
|
|
348
|
+
logs(kind) {
|
|
349
|
+
return [...this.children.get(kind)?.lines ?? []];
|
|
350
|
+
}
|
|
351
|
+
/** Every new line as it arrives. Returns the unsubscribe. */
|
|
352
|
+
onLog(kind, fn) {
|
|
353
|
+
const set = this.listeners.get(kind) ?? this.listeners.set(kind, /* @__PURE__ */ new Set()).get(kind);
|
|
354
|
+
set.add(fn);
|
|
355
|
+
return () => {
|
|
356
|
+
set.delete(fn);
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
view(kind) {
|
|
360
|
+
const child = this.children.get(kind);
|
|
361
|
+
const records = this.registryFor(kind);
|
|
362
|
+
const mine = child && child.state !== "exited" ? records.find((r) => r.pid === child.proc.pid) : void 0;
|
|
363
|
+
const others = records.filter((r) => r.pid !== child?.proc.pid).map((r) => ({ pid: r.pid, url: r.url, startedAt: r.startedAt, version: r.version, install: r.install }));
|
|
364
|
+
const activity = (r) => r ? { count: r.activityCount, last: r.activity, at: r.lastActivityAt, version: r.version, install: r.install } : void 0;
|
|
365
|
+
if (child) {
|
|
366
|
+
return {
|
|
367
|
+
kind,
|
|
368
|
+
state: child.state,
|
|
369
|
+
owned: true,
|
|
370
|
+
pid: child.proc.pid,
|
|
371
|
+
url: child.url ?? mine?.url,
|
|
372
|
+
startedAt: child.startedAt,
|
|
373
|
+
exitCode: child.exitCode,
|
|
374
|
+
error: child.error,
|
|
375
|
+
lines: child.lines.length,
|
|
376
|
+
token: child.token,
|
|
377
|
+
activity: activity(mine),
|
|
378
|
+
others
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
const first = records[0];
|
|
382
|
+
return first ? { kind, state: "running", owned: false, pid: first.pid, url: first.url, startedAt: first.startedAt, lines: 0, activity: activity(first), others: others.filter((o) => o.pid !== first.pid) } : { kind, state: "stopped", owned: false, lines: 0, others: [] };
|
|
383
|
+
}
|
|
384
|
+
list() {
|
|
385
|
+
return ["serve", "watch"].map((k) => this.view(k));
|
|
386
|
+
}
|
|
387
|
+
registryFor(kind) {
|
|
388
|
+
const here = real(this.projectDir);
|
|
389
|
+
return listServices(this.opts.registryDir).filter((s) => s.kind === kind && real(s.project ?? s.cwd) === here);
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
var real = (p) => {
|
|
393
|
+
try {
|
|
394
|
+
return fs2.realpathSync(p);
|
|
395
|
+
} catch {
|
|
396
|
+
return path2.resolve(p);
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
400
|
+
function terminate(proc) {
|
|
401
|
+
return new Promise((resolve9) => {
|
|
402
|
+
if (proc.exitCode !== null || proc.signalCode !== null) {
|
|
403
|
+
resolve9();
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
const timer = setTimeout(() => {
|
|
407
|
+
try {
|
|
408
|
+
proc.kill("SIGKILL");
|
|
409
|
+
} catch {
|
|
410
|
+
}
|
|
411
|
+
}, STOP_GRACE_MS);
|
|
412
|
+
proc.once("close", () => {
|
|
413
|
+
clearTimeout(timer);
|
|
414
|
+
resolve9();
|
|
415
|
+
});
|
|
416
|
+
try {
|
|
417
|
+
proc.kill("SIGTERM");
|
|
418
|
+
} catch {
|
|
419
|
+
clearTimeout(timer);
|
|
420
|
+
resolve9();
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// src/console/scan.ts
|
|
426
|
+
import * as fs3 from "node:fs";
|
|
427
|
+
import * as path3 from "node:path";
|
|
428
|
+
|
|
429
|
+
// src/console/packs.ts
|
|
430
|
+
function packNamespace(name) {
|
|
431
|
+
return name.replace(/^@[^/]+\//, "").replace(/^flow-weaver-pack-/, "");
|
|
432
|
+
}
|
|
433
|
+
var posix = (p) => p.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
434
|
+
function packForFile(file, packs) {
|
|
435
|
+
const f = posix(file);
|
|
436
|
+
let best = null;
|
|
437
|
+
for (const p of packs) {
|
|
438
|
+
const root = posix(p.path);
|
|
439
|
+
if ((f === root || f.startsWith(`${root}/`)) && (!best || root.length > posix(best.path).length)) best = p;
|
|
440
|
+
}
|
|
441
|
+
return best?.name ?? null;
|
|
442
|
+
}
|
|
443
|
+
function packageOfSpecifier(specifier) {
|
|
444
|
+
const parts = specifier.split("/");
|
|
445
|
+
if (!parts[0] || parts[0].startsWith(".")) return null;
|
|
446
|
+
return parts[0].startsWith("@") ? parts[1] ? `${parts[0]}/${parts[1]}` : null : parts[0];
|
|
447
|
+
}
|
|
448
|
+
function packForSpecifier(specifier, packs) {
|
|
449
|
+
const name = specifier ? packageOfSpecifier(specifier) : null;
|
|
450
|
+
return name && packs.some((p) => p.name === name) ? name : null;
|
|
451
|
+
}
|
|
452
|
+
var refsCache = /* @__PURE__ */ new Map();
|
|
453
|
+
async function installedRefs(projectDir) {
|
|
454
|
+
const hit = refsCache.get(projectDir);
|
|
455
|
+
if (hit && Date.now() - hit.at < 2e3) return hit.refs;
|
|
456
|
+
const refs = (await listInstalledPackages(projectDir)).map((p) => ({ name: p.name, path: p.path }));
|
|
457
|
+
refsCache.set(projectDir, { at: Date.now(), refs });
|
|
458
|
+
return refs;
|
|
459
|
+
}
|
|
460
|
+
var parse = (v) => {
|
|
461
|
+
const m = v.trim().match(/^v?(\d+)\.(\d+)\.(\d+)/);
|
|
462
|
+
return m ? [Number(m[1]), Number(m[2]), Number(m[3])] : null;
|
|
463
|
+
};
|
|
464
|
+
var cmp = (a, b) => a[0] - b[0] || a[1] - b[1] || a[2] - b[2];
|
|
465
|
+
function engineCompatible(range, version = VERSION) {
|
|
466
|
+
if (!range || range.trim() === "*") return true;
|
|
467
|
+
const have = parse(version);
|
|
468
|
+
const m = range.trim().match(/^(>=|\^|~)?\s*(v?\d+\.\d+\.\d+)$/);
|
|
469
|
+
const want = m ? parse(m[2]) : null;
|
|
470
|
+
if (!have || !want) return null;
|
|
471
|
+
const op = m[1] ?? "";
|
|
472
|
+
if (op === ">=") return cmp(have, want) >= 0;
|
|
473
|
+
if (op === "^") return want[0] === 0 ? have[0] === 0 && have[1] === want[1] && have[2] >= want[2] : have[0] === want[0] && cmp(have, want) >= 0;
|
|
474
|
+
if (op === "~") return have[0] === want[0] && have[1] === want[1] && have[2] >= want[2];
|
|
475
|
+
return cmp(have, want) === 0;
|
|
476
|
+
}
|
|
477
|
+
var CONTROL = /* @__PURE__ */ new Set(["execute", "onSuccess", "onFailure"]);
|
|
478
|
+
var ports = (map) => Object.entries(map ?? {}).filter(([name]) => !CONTROL.has(name)).map(([name, p]) => ({ name, type: String(p.dataType).toLowerCase(), optional: !!p.optional, description: p.description ?? "" }));
|
|
479
|
+
function describePack(pkg) {
|
|
480
|
+
const m = pkg.manifest;
|
|
481
|
+
const namespace = packNamespace(pkg.name);
|
|
482
|
+
return {
|
|
483
|
+
name: pkg.name,
|
|
484
|
+
namespace,
|
|
485
|
+
version: pkg.version,
|
|
486
|
+
path: pkg.path,
|
|
487
|
+
description: m.description ?? "",
|
|
488
|
+
engineVersion: m.engineVersion ?? null,
|
|
489
|
+
compatible: engineCompatible(m.engineVersion),
|
|
490
|
+
nodeTypes: (m.nodeTypes ?? []).map((n) => ({
|
|
491
|
+
name: n.name,
|
|
492
|
+
functionName: n.functionName,
|
|
493
|
+
description: n.description ?? "",
|
|
494
|
+
inputs: ports(n.inputs),
|
|
495
|
+
outputs: ports(n.outputs),
|
|
496
|
+
color: n.visuals?.color ?? null,
|
|
497
|
+
icon: n.visuals?.icon ?? null
|
|
498
|
+
})),
|
|
499
|
+
workflows: (m.workflows ?? []).map((w) => ({ name: w.name, description: w.description ?? "", params: ports(w.startPorts), returns: ports(w.exitPorts), nodes: w.nodeCount })),
|
|
500
|
+
exportTargets: (m.exportTargets ?? []).map((t) => ({ name: t.name, description: t.description ?? "" })),
|
|
501
|
+
tagHandlers: (m.tagHandlers ?? []).map((t) => ({ tags: t.tags, namespace: t.namespace, scope: t.scope })),
|
|
502
|
+
validationRuleSets: (m.validationRuleSets ?? []).map((r) => ({ name: r.name, namespace: r.namespace })),
|
|
503
|
+
docs: (m.docs ?? []).map((d) => ({ slug: d.slug, name: d.name, description: d.description ?? "" })),
|
|
504
|
+
// A pack with an entrypoint but no commands contributes none: the CLI skips it.
|
|
505
|
+
cliCommands: m.cliEntrypoint ? (m.cliCommands ?? []).map((c) => ({
|
|
506
|
+
name: c.name,
|
|
507
|
+
description: c.description,
|
|
508
|
+
usage: ["fw", namespace, c.name, ...c.arguments?.map((a) => a.syntax) ?? (c.usage ? [c.usage] : [])].join(" "),
|
|
509
|
+
flags: (c.options ?? []).map((o) => ({ flag: o.flags, description: o.description, default: o.default === void 0 ? "" : String(o.default) }))
|
|
510
|
+
})) : [],
|
|
511
|
+
mcpTools: m.mcpEntrypoint ? (m.mcpTools ?? []).map((t) => ({ name: t.name, description: t.description })) : []
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
async function describePacks(projectDir) {
|
|
515
|
+
const installed = await listInstalledPackages(projectDir);
|
|
516
|
+
return installed.map(describePack).sort((a, b) => a.namespace.localeCompare(b.namespace));
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// src/console/scan.ts
|
|
520
|
+
var SKIP = /* @__PURE__ */ new Set(["node_modules", "dist", ".git", ".fw", "coverage"]);
|
|
521
|
+
var toPosix = (p) => p.split(path3.sep).join("/");
|
|
522
|
+
function walk(dir, out) {
|
|
523
|
+
let entries;
|
|
524
|
+
try {
|
|
525
|
+
entries = fs3.readdirSync(dir, { withFileTypes: true });
|
|
526
|
+
} catch {
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
for (const entry of entries) {
|
|
530
|
+
if (SKIP.has(entry.name) || entry.name.startsWith(".")) continue;
|
|
531
|
+
const full = path3.join(dir, entry.name);
|
|
532
|
+
if (entry.isDirectory()) walk(full, out);
|
|
533
|
+
else if (entry.name.endsWith(".ts") && !entry.name.endsWith(".d.ts") && !entry.name.startsWith(".fw-diff-")) out.push(full);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
function listOne(file, projectDir, mtimeMs) {
|
|
537
|
+
let text;
|
|
538
|
+
try {
|
|
539
|
+
text = fs3.readFileSync(file, "utf8");
|
|
540
|
+
} catch {
|
|
541
|
+
return null;
|
|
542
|
+
}
|
|
543
|
+
if (!text.includes("@flowWeaver workflow")) return null;
|
|
544
|
+
const names2 = getAvailableWorkflows(text).map((w) => w.functionName).filter(Boolean);
|
|
545
|
+
return names2.length ? { file, rel: toPosix(path3.relative(projectDir, file)), names: names2, mtimeMs } : null;
|
|
546
|
+
}
|
|
547
|
+
var checked = /* @__PURE__ */ new Map();
|
|
548
|
+
var listings = /* @__PURE__ */ new Map();
|
|
549
|
+
function invalidateListing(projectDir) {
|
|
550
|
+
if (projectDir) listings.delete(path3.resolve(projectDir));
|
|
551
|
+
else listings.clear();
|
|
552
|
+
}
|
|
553
|
+
function listCached(projectDir) {
|
|
554
|
+
const key = path3.resolve(projectDir);
|
|
555
|
+
const files = [];
|
|
556
|
+
walk(key, files);
|
|
557
|
+
const stats = /* @__PURE__ */ new Map();
|
|
558
|
+
for (const file of files) {
|
|
559
|
+
try {
|
|
560
|
+
stats.set(file, fs3.statSync(file).mtimeMs);
|
|
561
|
+
} catch {
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
const signature = [...stats].map(([f, m]) => `${f}:${m}`).join("|");
|
|
565
|
+
const hit = listings.get(key);
|
|
566
|
+
if (hit && hit.signature === signature) return [...hit.byFile.values()];
|
|
567
|
+
const byFile = /* @__PURE__ */ new Map();
|
|
568
|
+
for (const [file, mtimeMs] of stats) {
|
|
569
|
+
const previous = hit?.byFile.get(file);
|
|
570
|
+
if (previous && previous.mtimeMs === mtimeMs) {
|
|
571
|
+
byFile.set(file, previous);
|
|
572
|
+
continue;
|
|
573
|
+
}
|
|
574
|
+
const listed = listOne(file, key, mtimeMs);
|
|
575
|
+
if (listed) byFile.set(file, listed);
|
|
576
|
+
}
|
|
577
|
+
listings.set(key, { byFile, signature });
|
|
578
|
+
return [...byFile.values()];
|
|
579
|
+
}
|
|
580
|
+
async function check(entry, name, packs) {
|
|
581
|
+
const base = {
|
|
582
|
+
file: entry.file,
|
|
583
|
+
rel: entry.rel,
|
|
584
|
+
name,
|
|
585
|
+
steps: 0,
|
|
586
|
+
gates: 0,
|
|
587
|
+
errors: 0,
|
|
588
|
+
warnings: 0,
|
|
589
|
+
checked: true,
|
|
590
|
+
codes: [],
|
|
591
|
+
uses: []
|
|
592
|
+
};
|
|
593
|
+
try {
|
|
594
|
+
const parsed = await parseWorkflow(entry.file, { workflowName: name, projectDir: path3.dirname(entry.file) });
|
|
595
|
+
if (parsed.errors.length || !parsed.ast) return { ...base, errors: Math.max(1, parsed.errors.length) };
|
|
596
|
+
const ast = parsed.ast;
|
|
597
|
+
const v = validateWorkflow(ast);
|
|
598
|
+
const uses = /* @__PURE__ */ new Set();
|
|
599
|
+
let gates = 0;
|
|
600
|
+
for (const i of ast.instances) {
|
|
601
|
+
const nt = ast.nodeTypes.find((n) => n.name === i.nodeType) ?? ast.nodeTypes.find((n) => n.functionName === i.nodeType);
|
|
602
|
+
if (nt?.durableGate !== void 0) {
|
|
603
|
+
gates++;
|
|
604
|
+
uses.add(`gate:${nt.durableGate}`);
|
|
605
|
+
}
|
|
606
|
+
if (nt && !nt.functionText) uses.add(`builtin:${nt.functionName}`);
|
|
607
|
+
const pack = (nt?.sourceLocation?.file ? packForFile(nt.sourceLocation.file, packs) : null) ?? packForSpecifier(nt?.importSource, packs);
|
|
608
|
+
if (pack) uses.add(`pack:${pack}`);
|
|
609
|
+
if (nt?.durableEffect) uses.add("effect");
|
|
610
|
+
if (nt?.durablePure) uses.add("pure");
|
|
611
|
+
if (nt?.expression) uses.add("expression");
|
|
612
|
+
if (i.config?.pullExecution !== void 0) uses.add("pull");
|
|
613
|
+
if (i.config?.portConfigs?.some((c) => c.expression)) uses.add("expr");
|
|
614
|
+
}
|
|
615
|
+
if (Object.keys(ast.scopes ?? {}).length) uses.add("scope");
|
|
616
|
+
const codes = [...new Set([...v.errors, ...v.warnings].map((e) => e.code))];
|
|
617
|
+
return { ...base, steps: ast.instances.length, gates, errors: v.errors.length, warnings: v.warnings.length, codes, uses: [...uses] };
|
|
618
|
+
} catch {
|
|
619
|
+
return { ...base, errors: 1 };
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
var sortSummaries = (rows) => rows.sort((a, b) => a.rel.localeCompare(b.rel) || a.name.localeCompare(b.name));
|
|
623
|
+
function scanWorkflowNames(projectDir) {
|
|
624
|
+
const rows = [];
|
|
625
|
+
for (const entry of listCached(projectDir)) {
|
|
626
|
+
for (const name of entry.names) {
|
|
627
|
+
const hit = checked.get(`${entry.file}|${name}`);
|
|
628
|
+
rows.push(hit && hit.mtimeMs === entry.mtimeMs ? hit.summary : { file: entry.file, rel: entry.rel, name, steps: 0, gates: 0, errors: 0, warnings: 0, checked: false, codes: [], uses: [] });
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
return sortSummaries(rows);
|
|
632
|
+
}
|
|
633
|
+
async function checkWorkflows(projectDir, onChecked) {
|
|
634
|
+
const rows = [];
|
|
635
|
+
const packs = await installedRefs(projectDir);
|
|
636
|
+
for (const entry of listCached(projectDir)) {
|
|
637
|
+
for (const name of entry.names) {
|
|
638
|
+
const key = `${entry.file}|${name}`;
|
|
639
|
+
const hit = checked.get(key);
|
|
640
|
+
if (hit && hit.mtimeMs === entry.mtimeMs) {
|
|
641
|
+
rows.push(hit.summary);
|
|
642
|
+
continue;
|
|
643
|
+
}
|
|
644
|
+
const summary = await check(entry, name, packs);
|
|
645
|
+
checked.set(key, { mtimeMs: entry.mtimeMs, summary });
|
|
646
|
+
rows.push(summary);
|
|
647
|
+
onChecked?.(summary);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
return sortSummaries(rows);
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
// src/console/runs.ts
|
|
654
|
+
import * as fs8 from "node:fs";
|
|
655
|
+
import * as path7 from "node:path";
|
|
656
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
657
|
+
|
|
658
|
+
// src/console/debug.ts
|
|
659
|
+
import * as fs4 from "node:fs";
|
|
660
|
+
import * as os2 from "node:os";
|
|
661
|
+
import * as path4 from "node:path";
|
|
662
|
+
import { randomUUID } from "node:crypto";
|
|
663
|
+
function latestValues(variables) {
|
|
664
|
+
const best = /* @__PURE__ */ new Map();
|
|
665
|
+
for (const [key, value] of Object.entries(variables)) {
|
|
666
|
+
const at2 = key.lastIndexOf(":");
|
|
667
|
+
const mid = key.lastIndexOf(":", at2 - 1);
|
|
668
|
+
if (at2 < 0 || mid < 0) continue;
|
|
669
|
+
const name = `${key.slice(0, mid)}.${key.slice(mid + 1, at2)}`;
|
|
670
|
+
const index = Number(key.slice(at2 + 1));
|
|
671
|
+
const cur = best.get(name);
|
|
672
|
+
if (!cur || index > cur.index) best.set(name, { index, value });
|
|
673
|
+
}
|
|
674
|
+
return Object.fromEntries([...best].map(([k, v]) => [k, v.value]));
|
|
675
|
+
}
|
|
676
|
+
function latestKey(variables, node, port) {
|
|
677
|
+
const prefix = `${node}:${port}:`;
|
|
678
|
+
let found = null;
|
|
679
|
+
let top = -1;
|
|
680
|
+
for (const key of Object.keys(variables)) {
|
|
681
|
+
if (!key.startsWith(prefix)) continue;
|
|
682
|
+
const index = Number(key.slice(prefix.length));
|
|
683
|
+
if (index > top) {
|
|
684
|
+
top = index;
|
|
685
|
+
found = key;
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
return found;
|
|
689
|
+
}
|
|
690
|
+
var DebugSessions = class {
|
|
691
|
+
sessions = /* @__PURE__ */ new Map();
|
|
692
|
+
get(id) {
|
|
693
|
+
return this.sessions.get(id)?.view;
|
|
694
|
+
}
|
|
695
|
+
list() {
|
|
696
|
+
return [...this.sessions.values()].map((s) => s.view);
|
|
697
|
+
}
|
|
698
|
+
/**
|
|
699
|
+
* Start a session. Resolves at the first pause, or when the run ends
|
|
700
|
+
* without one. The session is listed from the moment this is called, so
|
|
701
|
+
* a caller that answers with the id before the pause can show it.
|
|
702
|
+
*/
|
|
703
|
+
async start(options) {
|
|
704
|
+
const file = path4.resolve(options.file);
|
|
705
|
+
const id = options.id ?? randomUUID();
|
|
706
|
+
const controller = new DebugController({ debug: true, breakpoints: options.breakpoints });
|
|
707
|
+
const abort = new AbortController();
|
|
708
|
+
const effectsDir = fs4.mkdtempSync(path4.join(os2.tmpdir(), "fw-debug-"));
|
|
709
|
+
const view = {
|
|
710
|
+
id,
|
|
711
|
+
file,
|
|
712
|
+
name: options.name,
|
|
713
|
+
params: options.params ?? {},
|
|
714
|
+
status: "running",
|
|
715
|
+
startedAt: Date.now(),
|
|
716
|
+
updatedAt: Date.now(),
|
|
717
|
+
position: 0,
|
|
718
|
+
order: [],
|
|
719
|
+
breakpoints: controller.getBreakpoints(),
|
|
720
|
+
values: {}
|
|
721
|
+
};
|
|
722
|
+
const session = {
|
|
723
|
+
view,
|
|
724
|
+
controller,
|
|
725
|
+
abort,
|
|
726
|
+
done: Promise.resolve({ kind: "error", error: new Error("not started") }),
|
|
727
|
+
aborting: false,
|
|
728
|
+
effectsDir,
|
|
729
|
+
onChange: options.onChange
|
|
730
|
+
};
|
|
731
|
+
this.sessions.set(id, session);
|
|
732
|
+
const parsed = await parseWorkflow(file, { workflowName: options.name, projectDir: path4.dirname(file) });
|
|
733
|
+
if (parsed.errors.length) {
|
|
734
|
+
session.view = { ...view, status: "failed", error: parsed.errors.join("\n"), updatedAt: Date.now() };
|
|
735
|
+
fs4.rmSync(effectsDir, { recursive: true, force: true });
|
|
736
|
+
session.onChange?.(session.view);
|
|
737
|
+
return session.view;
|
|
738
|
+
}
|
|
739
|
+
const order = getTopologicalOrder(parsed.ast, { includeScopedChildren: true });
|
|
740
|
+
controller.setExecutionOrder(order);
|
|
741
|
+
session.view = { ...session.view, order };
|
|
742
|
+
let bundleDigest;
|
|
743
|
+
try {
|
|
744
|
+
bundleDigest = await computeBundleDigest(file, options.name);
|
|
745
|
+
} catch {
|
|
746
|
+
}
|
|
747
|
+
session.done = executeWorkflow({
|
|
748
|
+
runId: id,
|
|
749
|
+
filePath: file,
|
|
750
|
+
workflowName: options.name,
|
|
751
|
+
params: options.params,
|
|
752
|
+
includeTrace: true,
|
|
753
|
+
onEvent: options.onEvent,
|
|
754
|
+
debugController: controller,
|
|
755
|
+
abortSignal: abort.signal,
|
|
756
|
+
mocks: options.mocks,
|
|
757
|
+
bundleDigest,
|
|
758
|
+
effectAdapter: createFileEffectAdapter(effectsDir)
|
|
759
|
+
}).then((outcome) => ({ kind: "outcome", outcome })).catch((error) => ({ kind: "error", error }));
|
|
760
|
+
await this.settle(session);
|
|
761
|
+
return session.view;
|
|
762
|
+
}
|
|
763
|
+
/** Run the next node and pause again. */
|
|
764
|
+
step(id) {
|
|
765
|
+
return this.act(id, { type: "step" });
|
|
766
|
+
}
|
|
767
|
+
/** Run to the end, or to the next breakpoint. */
|
|
768
|
+
continue(id, toBreakpoint = false) {
|
|
769
|
+
return this.act(id, { type: toBreakpoint ? "continueToBreakpoint" : "continue" });
|
|
770
|
+
}
|
|
771
|
+
/** Stop the session, paused or not. */
|
|
772
|
+
async abort(id) {
|
|
773
|
+
const s = this.need(id);
|
|
774
|
+
s.aborting = true;
|
|
775
|
+
if (s.view.status === "paused") return this.act(id, { type: "abort" });
|
|
776
|
+
s.abort.abort();
|
|
777
|
+
await this.settle(s);
|
|
778
|
+
return s.view;
|
|
779
|
+
}
|
|
780
|
+
/**
|
|
781
|
+
* Change a value a node produced. Takes effect before the next node runs,
|
|
782
|
+
* which is how a branch is forced or a bad value patched without a rerun.
|
|
783
|
+
*/
|
|
784
|
+
setVariable(id, node, port, value) {
|
|
785
|
+
const s = this.need(id);
|
|
786
|
+
if (s.view.status !== "paused" || !s.last) throw new Error("the session is not paused");
|
|
787
|
+
const key = latestKey(s.last.variables, node, port);
|
|
788
|
+
if (!key) throw new Error(`${node}.${port} has no value yet. It can be set once ${node} has run`);
|
|
789
|
+
s.controller.setVariable(key, value);
|
|
790
|
+
s.last.variables[key] = value;
|
|
791
|
+
s.view = { ...s.view, values: { ...s.view.values, [`${node}.${port}`]: value }, updatedAt: Date.now() };
|
|
792
|
+
s.onChange?.(s.view);
|
|
793
|
+
return s.view;
|
|
794
|
+
}
|
|
795
|
+
breakpoint(id, action, node) {
|
|
796
|
+
const s = this.need(id);
|
|
797
|
+
if (action === "add") s.controller.addBreakpoint(node);
|
|
798
|
+
else s.controller.removeBreakpoint(node);
|
|
799
|
+
s.view = { ...s.view, breakpoints: s.controller.getBreakpoints(), updatedAt: Date.now() };
|
|
800
|
+
s.onChange?.(s.view);
|
|
801
|
+
return s.view;
|
|
802
|
+
}
|
|
803
|
+
need(id) {
|
|
804
|
+
const s = this.sessions.get(id);
|
|
805
|
+
if (!s) throw new Error(`no debug session ${id}`);
|
|
806
|
+
return s;
|
|
807
|
+
}
|
|
808
|
+
async act(id, action) {
|
|
809
|
+
const s = this.need(id);
|
|
810
|
+
if (s.view.status !== "paused") throw new Error(`the session is ${s.view.status}, not paused`);
|
|
811
|
+
s.view = { ...s.view, status: "running", node: void 0, phase: void 0, updatedAt: Date.now() };
|
|
812
|
+
s.onChange?.(s.view);
|
|
813
|
+
s.controller.resume(action);
|
|
814
|
+
await this.settle(s);
|
|
815
|
+
return s.view;
|
|
816
|
+
}
|
|
817
|
+
/** Wait for the next pause or the end, and record which it was. */
|
|
818
|
+
async settle(s) {
|
|
819
|
+
const paused = s.controller.onPause().then((state) => ({ kind: "paused", state }));
|
|
820
|
+
const next = await Promise.race([s.done, paused]);
|
|
821
|
+
const now = Date.now();
|
|
822
|
+
if (next.kind === "paused") {
|
|
823
|
+
s.last = next.state;
|
|
824
|
+
s.view = {
|
|
825
|
+
...s.view,
|
|
826
|
+
status: "paused",
|
|
827
|
+
node: next.state.currentNodeId,
|
|
828
|
+
phase: next.state.phase,
|
|
829
|
+
position: next.state.position,
|
|
830
|
+
breakpoints: next.state.breakpoints,
|
|
831
|
+
values: latestValues(next.state.variables),
|
|
832
|
+
updatedAt: now
|
|
833
|
+
};
|
|
834
|
+
} else if (next.kind === "outcome" && next.outcome.kind === "completed") {
|
|
835
|
+
s.view = { ...s.view, status: "completed", node: void 0, phase: void 0, position: s.view.order.length, result: next.outcome.result, updatedAt: now };
|
|
836
|
+
} else if (next.kind === "outcome" && next.outcome.kind === "yielded") {
|
|
837
|
+
const gate = next.outcome.gate.address.nodeId;
|
|
838
|
+
s.view = {
|
|
839
|
+
...s.view,
|
|
840
|
+
status: "yielded",
|
|
841
|
+
node: gate,
|
|
842
|
+
phase: void 0,
|
|
843
|
+
updatedAt: now,
|
|
844
|
+
error: `Reached the gate at ${gate}. The debugger cannot hold a gate. Run the workflow without it to answer one.`
|
|
845
|
+
};
|
|
846
|
+
} else {
|
|
847
|
+
const error = next.kind === "error" ? next.error : void 0;
|
|
848
|
+
const message = getErrorMessage(error);
|
|
849
|
+
const aborted = s.aborting || s.abort.signal.aborted || /Debug session aborted/.test(message);
|
|
850
|
+
s.view = { ...s.view, status: aborted ? "aborted" : "failed", node: void 0, phase: void 0, error: aborted ? void 0 : message, updatedAt: now };
|
|
851
|
+
}
|
|
852
|
+
if (s.view.status !== "paused" && s.view.status !== "running") fs4.rmSync(s.effectsDir, { recursive: true, force: true });
|
|
853
|
+
s.onChange?.(s.view);
|
|
854
|
+
}
|
|
855
|
+
};
|
|
856
|
+
|
|
857
|
+
// src/console/workflow-view.ts
|
|
858
|
+
import * as fs6 from "node:fs";
|
|
859
|
+
import * as path6 from "node:path";
|
|
860
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
861
|
+
|
|
862
|
+
// src/console/git.ts
|
|
863
|
+
import { execFile } from "node:child_process";
|
|
864
|
+
import * as fs5 from "node:fs";
|
|
865
|
+
import * as path5 from "node:path";
|
|
866
|
+
function git(cwd, args) {
|
|
867
|
+
return new Promise((resolve9, reject) => {
|
|
868
|
+
execFile("git", args, { cwd, maxBuffer: 16 * 1024 * 1024, windowsHide: true }, (err, stdout) => {
|
|
869
|
+
if (err) reject(err instanceof Error ? err : new Error(String(err)));
|
|
870
|
+
else resolve9(String(stdout));
|
|
871
|
+
});
|
|
872
|
+
});
|
|
873
|
+
}
|
|
874
|
+
async function repoRoot(file) {
|
|
875
|
+
try {
|
|
876
|
+
return (await git(path5.dirname(file), ["rev-parse", "--show-toplevel"])).trim() || void 0;
|
|
877
|
+
} catch {
|
|
878
|
+
return void 0;
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
function gitPath(root, file) {
|
|
882
|
+
const real2 = (p) => {
|
|
883
|
+
try {
|
|
884
|
+
return fs5.realpathSync(p);
|
|
885
|
+
} catch {
|
|
886
|
+
return p;
|
|
887
|
+
}
|
|
888
|
+
};
|
|
889
|
+
return path5.relative(real2(root), real2(file)).split(path5.sep).join("/");
|
|
890
|
+
}
|
|
891
|
+
async function fileHistory(file, limit = 60) {
|
|
892
|
+
const root = await repoRoot(file);
|
|
893
|
+
if (!root) return { repo: false, dirty: false, commits: [] };
|
|
894
|
+
const rel = gitPath(root, file);
|
|
895
|
+
const [head, status, log] = await Promise.all([
|
|
896
|
+
git(root, ["rev-parse", "--short", "HEAD"]).then((s) => s.trim()).catch(() => void 0),
|
|
897
|
+
git(root, ["status", "--porcelain", "--", rel]).catch(() => ""),
|
|
898
|
+
git(root, ["log", "--follow", `-n${limit}`, "--format=%H%x1f%h%x1f%an%x1f%at%x1f%s", "--", rel]).catch(() => "")
|
|
899
|
+
]);
|
|
900
|
+
const commits = log.split("\n").filter(Boolean).map((line) => {
|
|
901
|
+
const [sha, short, author, at2, subject] = line.split("");
|
|
902
|
+
return { sha, short, author, at: Number(at2) * 1e3, subject };
|
|
903
|
+
});
|
|
904
|
+
return { repo: true, head, dirty: status.trim().length > 0, commits };
|
|
905
|
+
}
|
|
906
|
+
async function fileAt(file, ref) {
|
|
907
|
+
const root = await repoRoot(file);
|
|
908
|
+
if (!root) return void 0;
|
|
909
|
+
try {
|
|
910
|
+
return await git(root, ["show", `${ref}:${gitPath(root, file)}`]);
|
|
911
|
+
} catch {
|
|
912
|
+
return void 0;
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
async function stamp(file) {
|
|
916
|
+
const root = await repoRoot(file);
|
|
917
|
+
if (!root) return void 0;
|
|
918
|
+
const rel = gitPath(root, file);
|
|
919
|
+
const [commit, status] = await Promise.all([
|
|
920
|
+
git(root, ["rev-parse", "--short", "HEAD"]).then((s) => s.trim()).catch(() => void 0),
|
|
921
|
+
git(root, ["status", "--porcelain", "--", rel]).catch(() => "")
|
|
922
|
+
]);
|
|
923
|
+
return commit ? { commit, dirty: status.trim().length > 0 } : void 0;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
// src/console/source.ts
|
|
927
|
+
function findDeclaration(text, fnName) {
|
|
928
|
+
const re = new RegExp(`\\bexport\\s+(?:async\\s+)?function\\s+${fnName}\\s*[(<]`);
|
|
929
|
+
return text.search(re);
|
|
930
|
+
}
|
|
931
|
+
function endOfSignature(text, from) {
|
|
932
|
+
let depth = 0;
|
|
933
|
+
let closedParams = false;
|
|
934
|
+
let inReturnType = false;
|
|
935
|
+
const i0 = text.indexOf("(", from);
|
|
936
|
+
if (i0 < 0) return -1;
|
|
937
|
+
let i = i0;
|
|
938
|
+
for (; i < text.length; i++) {
|
|
939
|
+
const c = text[i];
|
|
940
|
+
if (c === "/" && text[i + 1] === "/") {
|
|
941
|
+
const nl = text.indexOf("\n", i);
|
|
942
|
+
if (nl < 0) return -1;
|
|
943
|
+
i = nl;
|
|
944
|
+
continue;
|
|
945
|
+
}
|
|
946
|
+
if (c === "/" && text[i + 1] === "*") {
|
|
947
|
+
const end = text.indexOf("*/", i + 2);
|
|
948
|
+
if (end < 0) return -1;
|
|
949
|
+
i = end + 1;
|
|
950
|
+
continue;
|
|
951
|
+
}
|
|
952
|
+
if (c === "'" || c === '"' || c === "`") {
|
|
953
|
+
const quote = c;
|
|
954
|
+
i++;
|
|
955
|
+
while (i < text.length && text[i] !== quote) {
|
|
956
|
+
if (text[i] === "\\") i++;
|
|
957
|
+
i++;
|
|
958
|
+
}
|
|
959
|
+
continue;
|
|
960
|
+
}
|
|
961
|
+
if (c === ":" && depth === 0 && closedParams) {
|
|
962
|
+
inReturnType = true;
|
|
963
|
+
continue;
|
|
964
|
+
}
|
|
965
|
+
if (c === "(" || c === "[" || c === "{" || c === "<") {
|
|
966
|
+
if (c === "{" && depth === 0 && closedParams && !inReturnType) return i + 1;
|
|
967
|
+
depth++;
|
|
968
|
+
continue;
|
|
969
|
+
}
|
|
970
|
+
if (c === ")" || c === "]" || c === "}" || c === ">") {
|
|
971
|
+
if (c === ">" && text[i - 1] === "=") continue;
|
|
972
|
+
depth--;
|
|
973
|
+
if (c === ")" && depth === 0) closedParams = true;
|
|
974
|
+
if (depth === 0 && inReturnType && c !== ")") inReturnType = false;
|
|
975
|
+
continue;
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
return -1;
|
|
979
|
+
}
|
|
980
|
+
function workflowSource(text, fnName) {
|
|
981
|
+
const declaration = findDeclaration(text, fnName);
|
|
982
|
+
if (declaration < 0) return { source: "", line: 1 };
|
|
983
|
+
const bodyStart = endOfSignature(text, declaration);
|
|
984
|
+
const end = bodyStart < 0 ? text.length : bodyStart;
|
|
985
|
+
const commentStart = text.lastIndexOf("/**", declaration);
|
|
986
|
+
const between = commentStart >= 0 ? text.slice(text.indexOf("*/", commentStart) + 2, declaration) : "";
|
|
987
|
+
const attached = commentStart >= 0 && /^\s*$/.test(between);
|
|
988
|
+
const from = attached ? commentStart : declaration;
|
|
989
|
+
return {
|
|
990
|
+
source: text.slice(from, end).trimEnd(),
|
|
991
|
+
line: text.slice(0, from).split("\n").length
|
|
992
|
+
};
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
// src/console/terminals.ts
|
|
996
|
+
var CONTROL2 = /* @__PURE__ */ new Set(["execute", "onSuccess", "onFailure"]);
|
|
997
|
+
function terminalWiring(ast) {
|
|
998
|
+
const start = {};
|
|
999
|
+
const exit = {};
|
|
1000
|
+
for (const c of ast.connections) {
|
|
1001
|
+
if (c.from.node === "Start" && !CONTROL2.has(c.from.port)) {
|
|
1002
|
+
(start[c.from.port] ??= []).push({ node: c.to.node, port: c.to.port });
|
|
1003
|
+
}
|
|
1004
|
+
if (c.to.node === "Exit" && !CONTROL2.has(c.to.port) && !exit[c.to.port]) {
|
|
1005
|
+
exit[c.to.port] = { node: c.from.node, port: c.from.port };
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
return { start, exit };
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
// src/console/workflow-view.ts
|
|
1012
|
+
var CONTROL3 = /* @__PURE__ */ new Set(["execute", "onSuccess", "onFailure"]);
|
|
1013
|
+
function nodeTypeOf(ast, inst) {
|
|
1014
|
+
return ast.nodeTypes.find((n) => n.name === inst.nodeType) ?? ast.nodeTypes.find((n) => n.functionName === inst.nodeType);
|
|
1015
|
+
}
|
|
1016
|
+
function ports2(map) {
|
|
1017
|
+
return Object.entries(map ?? {}).filter(([k, p]) => !CONTROL3.has(k) && !p.isControlFlow).map(([k, p]) => ({ name: k, tsType: p.tsType ?? String(p.dataType).toLowerCase(), optional: !!p.optional, description: p.description ?? "" }));
|
|
1018
|
+
}
|
|
1019
|
+
async function parseOne(file, name) {
|
|
1020
|
+
const p = await parseWorkflow(file, { workflowName: name, projectDir: path6.dirname(file) });
|
|
1021
|
+
return { ast: p.errors.length ? void 0 : p.ast, errors: p.errors };
|
|
1022
|
+
}
|
|
1023
|
+
function describeIssue(e) {
|
|
1024
|
+
return {
|
|
1025
|
+
severity: e.type,
|
|
1026
|
+
code: e.code,
|
|
1027
|
+
message: e.message,
|
|
1028
|
+
node: e.node ?? null,
|
|
1029
|
+
line: e.location?.line ?? null,
|
|
1030
|
+
hint: ERROR_HINTS[e.code] ? ERROR_HINTS[e.code].replace(/<nodeId>/g, e.node ?? "<nodeId>") : null
|
|
1031
|
+
};
|
|
1032
|
+
}
|
|
1033
|
+
async function astAt(file, name, ref) {
|
|
1034
|
+
if (ref === "worktree") {
|
|
1035
|
+
const p = await parseOne(file, name);
|
|
1036
|
+
return p.ast ? { ast: p.ast } : { error: p.errors.join("\n") || "the file does not parse" };
|
|
1037
|
+
}
|
|
1038
|
+
const text = await fileAt(file, ref);
|
|
1039
|
+
if (text === void 0) return { error: `${path6.basename(file)} does not exist at ${ref}` };
|
|
1040
|
+
const tmp = path6.join(path6.dirname(file), `.fw-diff-${randomUUID2().slice(0, 8)}-${path6.basename(file)}`);
|
|
1041
|
+
fs6.writeFileSync(tmp, text, "utf8");
|
|
1042
|
+
try {
|
|
1043
|
+
const p = await parseWorkflow(tmp, { workflowName: name, projectDir: path6.dirname(file) });
|
|
1044
|
+
if (p.errors.length) return { error: `${path6.basename(file)} at ${ref} does not parse: ${p.errors[0]}` };
|
|
1045
|
+
return { ast: { ...p.ast, sourceFile: file } };
|
|
1046
|
+
} finally {
|
|
1047
|
+
fs6.rmSync(tmp, { force: true });
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
async function describeWorkflow(projectDir, file, name) {
|
|
1051
|
+
const text = fs6.readFileSync(file, "utf8");
|
|
1052
|
+
const rel = toPosix(path6.relative(projectDir, file));
|
|
1053
|
+
const { ast, errors } = await parseOne(file, name);
|
|
1054
|
+
if (!ast) return { file, rel, name, parseErrors: errors };
|
|
1055
|
+
const v = validateWorkflow(ast);
|
|
1056
|
+
const packs = await installedRefs(projectDir);
|
|
1057
|
+
const nodes = {};
|
|
1058
|
+
for (const inst of ast.instances) {
|
|
1059
|
+
const nt = nodeTypeOf(ast, inst);
|
|
1060
|
+
const nodeFile = nt?.sourceLocation?.file ?? file;
|
|
1061
|
+
nodes[inst.id] = {
|
|
1062
|
+
id: inst.id,
|
|
1063
|
+
type: inst.nodeType,
|
|
1064
|
+
label: stepLabel(inst, nt),
|
|
1065
|
+
description: nt?.description ?? "",
|
|
1066
|
+
builtin: !nt?.functionText,
|
|
1067
|
+
color: inst.config?.color ?? nt?.visuals?.color ?? null,
|
|
1068
|
+
icon: inst.config?.icon ?? nt?.visuals?.icon ?? null,
|
|
1069
|
+
pull: inst.config?.pullExecution !== void 0,
|
|
1070
|
+
source: nt?.functionText ?? "",
|
|
1071
|
+
file: nodeFile,
|
|
1072
|
+
line: nt?.sourceLocation?.line ?? null,
|
|
1073
|
+
gate: nt?.durableGate ?? null,
|
|
1074
|
+
// Where the node type came from, and what a pack's tags said about
|
|
1075
|
+
// this step: the parser resolves both and then shows neither.
|
|
1076
|
+
pack: packForFile(nodeFile, packs) ?? packForSpecifier(nt?.importSource, packs),
|
|
1077
|
+
deploy: Object.keys({ ...nt?.deploy, ...inst.deploy }).length ? { ...nt?.deploy, ...inst.deploy } : null,
|
|
1078
|
+
// What a step can and cannot do is the thing an author gets wrong:
|
|
1079
|
+
// an expression node has no onFailure, so a throw aborts the run,
|
|
1080
|
+
// and only a pure node may re-run when a gated workflow resumes.
|
|
1081
|
+
expression: !!nt?.expression,
|
|
1082
|
+
durablePure: !!nt?.durablePure,
|
|
1083
|
+
effect: !!nt?.durableEffect,
|
|
1084
|
+
async: !!nt?.isAsync,
|
|
1085
|
+
inputs: ports2(nt?.inputs),
|
|
1086
|
+
outputs: ports2(nt?.outputs),
|
|
1087
|
+
outputSchema: nt?.durableGate !== void 0 ? gateOutputSchemas(ast, inst.id, file) : null,
|
|
1088
|
+
expr: (inst.config?.portConfigs ?? []).filter((c) => c.expression).map((c) => ({ port: c.portName, expr: c.expression }))
|
|
1089
|
+
};
|
|
1090
|
+
}
|
|
1091
|
+
const model = buildProcessModel(ast);
|
|
1092
|
+
const stamp2 = (steps) => steps.forEach((s) => {
|
|
1093
|
+
s.label = nodes[s.id]?.label ?? s.label;
|
|
1094
|
+
s.pull = !!nodes[s.id]?.pull;
|
|
1095
|
+
stamp2(s.children);
|
|
1096
|
+
});
|
|
1097
|
+
stamp2(model.steps);
|
|
1098
|
+
const ws = workflowSource(text, ast.functionName);
|
|
1099
|
+
return {
|
|
1100
|
+
file,
|
|
1101
|
+
rel,
|
|
1102
|
+
name: ast.functionName,
|
|
1103
|
+
description: ast.description ?? "",
|
|
1104
|
+
compiled: hasInPlaceMarkers(text),
|
|
1105
|
+
params: ports2(ast.startPorts),
|
|
1106
|
+
paramsSchema: workflowParamsSchema(file, ast.functionName),
|
|
1107
|
+
returns: ports2(ast.exitPorts),
|
|
1108
|
+
// Port level, for the Start and Exit cards: which input each parameter
|
|
1109
|
+
// feeds, and which output becomes each return value.
|
|
1110
|
+
wiring: terminalWiring(ast),
|
|
1111
|
+
model,
|
|
1112
|
+
nodes,
|
|
1113
|
+
issues: [...v.errors, ...v.warnings].map(describeIssue),
|
|
1114
|
+
source: ws.source,
|
|
1115
|
+
sourceLine: ws.line,
|
|
1116
|
+
deploy: ast.options?.deploy && Object.keys(ast.options.deploy).length ? ast.options.deploy : null,
|
|
1117
|
+
// The routes the workflow declares with @http, for the Serve pane.
|
|
1118
|
+
http: ast.options?.http ?? [],
|
|
1119
|
+
reference: referenceOf(ast)
|
|
1120
|
+
};
|
|
1121
|
+
}
|
|
1122
|
+
var HTTP_METHODS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE"]);
|
|
1123
|
+
function routeFrom(raw) {
|
|
1124
|
+
if (typeof raw !== "object" || raw === null) return "a route is an object";
|
|
1125
|
+
const r = raw;
|
|
1126
|
+
const method = String(r.method ?? "").toUpperCase();
|
|
1127
|
+
if (!HTTP_METHODS.has(method)) return `method must be one of ${[...HTTP_METHODS].join(", ")}`;
|
|
1128
|
+
const p = String(r.path ?? "").trim();
|
|
1129
|
+
if (!p.startsWith("/") || /\s/.test(p)) return "a path starts with / and has no spaces";
|
|
1130
|
+
if (RESERVED_PATHS.some((x) => p === x || p.startsWith(`${x}/`))) return `${p} is under a reserved path (${RESERVED_PATHS.join(", ")})`;
|
|
1131
|
+
const route = { method, path: p };
|
|
1132
|
+
if (r.mode === "async") route.mode = "async";
|
|
1133
|
+
if (r.auth === "none") route.auth = "none";
|
|
1134
|
+
if (r.callback === true) route.callback = true;
|
|
1135
|
+
return route;
|
|
1136
|
+
}
|
|
1137
|
+
async function setHttpRoutes(file, name, routes) {
|
|
1138
|
+
const before = fs6.readFileSync(file, "utf8");
|
|
1139
|
+
const ws = workflowSource(before, name);
|
|
1140
|
+
if (!ws.source) return { ok: false, error: `${name} was not found in ${path6.basename(file)}` };
|
|
1141
|
+
const start = before.indexOf(ws.source);
|
|
1142
|
+
const block = ws.source;
|
|
1143
|
+
const marker = block.match(/^([ \t]*\*[ \t]*)@flowWeaver[ \t]+workflow[^\n]*\n/m);
|
|
1144
|
+
if (!marker || marker.index === void 0) return { ok: false, error: `${name} has no @flowWeaver workflow line` };
|
|
1145
|
+
const kept = block.replace(/^[ \t]*\*[ \t]*@http\b[^\n]*\n/gm, "");
|
|
1146
|
+
const at2 = kept.match(/^([ \t]*\*[ \t]*)@flowWeaver[ \t]+workflow[^\n]*\n/m);
|
|
1147
|
+
const insertAt = at2.index + at2[0].length;
|
|
1148
|
+
const lines = routes.map((r) => `${at2[1]}@http ${httpRouteText(r)}
|
|
1149
|
+
`).join("");
|
|
1150
|
+
const rewritten = kept.slice(0, insertAt) + lines + kept.slice(insertAt);
|
|
1151
|
+
const after = before.slice(0, start) + rewritten + before.slice(start + block.length);
|
|
1152
|
+
fs6.writeFileSync(file, after, "utf8");
|
|
1153
|
+
const p = await parseOne(file, name);
|
|
1154
|
+
if (!p.ast) {
|
|
1155
|
+
fs6.writeFileSync(file, before, "utf8");
|
|
1156
|
+
return { ok: false, error: `the file no longer parsed, so it was put back: ${p.errors[0] ?? "unknown error"}` };
|
|
1157
|
+
}
|
|
1158
|
+
return { ok: true };
|
|
1159
|
+
}
|
|
1160
|
+
function referenceOf(ast) {
|
|
1161
|
+
const { deploy: _deploy, ...options } = ast.options ?? {};
|
|
1162
|
+
const macros = ast.macros ?? [];
|
|
1163
|
+
const paths = macros.filter((m) => m.type === "path").map((m) => m.steps);
|
|
1164
|
+
const connects = ast.connections.filter((c) => !c.derived && !(macros.length && isConnectionCoveredByMacroStatic(c, macros))).map((c) => ({ from: { node: c.from.node, port: c.from.port }, to: { node: c.to.node, port: c.to.port } }));
|
|
1165
|
+
const exprs = ast.instances.flatMap((i) => (i.config?.portConfigs ?? []).filter((p) => p.expression).map((p) => ({ node: i.id, port: p.portName, expr: p.expression })));
|
|
1166
|
+
const importsFrom = ast.nodeTypes.filter((nt) => nt.importSource).map((nt) => ({ type: nt.name, from: nt.importSource }));
|
|
1167
|
+
return { options, paths, connects, exprs, importsFrom };
|
|
1168
|
+
}
|
|
1169
|
+
function crumbsFor(at2) {
|
|
1170
|
+
const { root } = path6.parse(at2);
|
|
1171
|
+
const rest = at2.slice(root.length).split(path6.sep).filter(Boolean);
|
|
1172
|
+
const crumbs = [];
|
|
1173
|
+
let dir = root;
|
|
1174
|
+
for (const name of rest) {
|
|
1175
|
+
dir = path6.join(dir, name);
|
|
1176
|
+
crumbs.push({ name, dir });
|
|
1177
|
+
}
|
|
1178
|
+
return crumbs;
|
|
1179
|
+
}
|
|
1180
|
+
function errorCodeSection(code) {
|
|
1181
|
+
const section = readTopicStructured("error-codes")?.sections.find((s) => s.heading.includes(code));
|
|
1182
|
+
return section ? { heading: section.heading, content: section.content, codeBlocks: section.codeBlocks } : null;
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
// src/console/respond.ts
|
|
1186
|
+
import * as fs7 from "node:fs";
|
|
1187
|
+
function json(res, status, body) {
|
|
1188
|
+
res.writeHead(status, { "Content-Type": "application/json" });
|
|
1189
|
+
res.end(JSON.stringify(body));
|
|
1190
|
+
}
|
|
1191
|
+
function sse(res) {
|
|
1192
|
+
res.writeHead(200, { "Content-Type": "text/event-stream", "Cache-Control": "no-cache", Connection: "keep-alive" });
|
|
1193
|
+
res.write(": ok\n\n");
|
|
1194
|
+
}
|
|
1195
|
+
function send(res, msg) {
|
|
1196
|
+
res.write(`data: ${JSON.stringify(msg)}
|
|
1197
|
+
|
|
1198
|
+
`);
|
|
1199
|
+
}
|
|
1200
|
+
function sendFile(res, file, type) {
|
|
1201
|
+
res.writeHead(200, { "Content-Type": type, "Cache-Control": "no-cache" });
|
|
1202
|
+
res.end(fs7.readFileSync(file));
|
|
1203
|
+
}
|
|
1204
|
+
var BadRequest = class extends Error {
|
|
1205
|
+
};
|
|
1206
|
+
var MAX_BODY = 1024 * 1024;
|
|
1207
|
+
async function readBody(req) {
|
|
1208
|
+
let s = "";
|
|
1209
|
+
for await (const c of req) {
|
|
1210
|
+
s += c;
|
|
1211
|
+
if (s.length > MAX_BODY) throw new BadRequest(`the body may not exceed ${MAX_BODY} bytes`);
|
|
1212
|
+
}
|
|
1213
|
+
if (!s.trim()) return {};
|
|
1214
|
+
let parsed;
|
|
1215
|
+
try {
|
|
1216
|
+
parsed = JSON.parse(s);
|
|
1217
|
+
} catch {
|
|
1218
|
+
throw new BadRequest("the body is not valid JSON");
|
|
1219
|
+
}
|
|
1220
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new BadRequest("the body must be a JSON object");
|
|
1221
|
+
return parsed;
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
// src/console/runs.ts
|
|
1225
|
+
function mocksFrom(b) {
|
|
1226
|
+
const m = b.mocks;
|
|
1227
|
+
if (!m || typeof m !== "object" || Array.isArray(m)) return void 0;
|
|
1228
|
+
const src = m;
|
|
1229
|
+
const out = {};
|
|
1230
|
+
for (const k of ["events", "agents", "invocations", "gates"]) {
|
|
1231
|
+
if (src[k] && typeof src[k] === "object" && !Array.isArray(src[k]) && Object.keys(src[k]).length) out[k] = src[k];
|
|
1232
|
+
}
|
|
1233
|
+
if (src.fast === true) out.fast = true;
|
|
1234
|
+
return Object.keys(out).length ? out : void 0;
|
|
1235
|
+
}
|
|
1236
|
+
var at = (iso) => Date.parse(iso);
|
|
1237
|
+
var DEBUG_STATUS = {
|
|
1238
|
+
running: "running",
|
|
1239
|
+
paused: "running",
|
|
1240
|
+
completed: "completed",
|
|
1241
|
+
failed: "failed",
|
|
1242
|
+
aborted: "cancelled",
|
|
1243
|
+
yielded: "cancelled"
|
|
1244
|
+
};
|
|
1245
|
+
function debugSnapshot(d, source) {
|
|
1246
|
+
return {
|
|
1247
|
+
id: d.id,
|
|
1248
|
+
file: d.file,
|
|
1249
|
+
name: d.name,
|
|
1250
|
+
params: d.params,
|
|
1251
|
+
source,
|
|
1252
|
+
status: DEBUG_STATUS[d.status],
|
|
1253
|
+
startedAt: d.startedAt,
|
|
1254
|
+
updatedAt: d.updatedAt,
|
|
1255
|
+
result: d.result,
|
|
1256
|
+
error: d.error,
|
|
1257
|
+
traced: true,
|
|
1258
|
+
debug: { status: d.status, node: d.node, phase: d.phase, position: d.position, order: d.order, breakpoints: d.breakpoints }
|
|
1259
|
+
};
|
|
1260
|
+
}
|
|
1261
|
+
function createRunDriver({ coordinator, broadcast, projectDir, profiles }) {
|
|
1262
|
+
const live = /* @__PURE__ */ new Map();
|
|
1263
|
+
const debug = new DebugSessions();
|
|
1264
|
+
const debugEvents = /* @__PURE__ */ new Map();
|
|
1265
|
+
const debugSource = /* @__PURE__ */ new Map();
|
|
1266
|
+
const agentEvents = /* @__PURE__ */ new Map();
|
|
1267
|
+
const subs = /* @__PURE__ */ new Map();
|
|
1268
|
+
const push = (id, msg) => {
|
|
1269
|
+
for (const res of subs.get(id) ?? []) send(res, msg);
|
|
1270
|
+
};
|
|
1271
|
+
let listed;
|
|
1272
|
+
const storeList = async () => {
|
|
1273
|
+
if (!listed || Date.now() - listed.at > 500) listed = { at: Date.now(), rows: await coordinator.list() };
|
|
1274
|
+
return listed.rows;
|
|
1275
|
+
};
|
|
1276
|
+
let lastCounts = /* @__PURE__ */ new Map();
|
|
1277
|
+
const waitingCounts = async () => {
|
|
1278
|
+
const counts = /* @__PURE__ */ new Map();
|
|
1279
|
+
for (const s of await storeList()) if (s.status === "waiting") counts.set(`${s.filePath}|${s.workflowName}`, (counts.get(`${s.filePath}|${s.workflowName}`) ?? 0) + 1);
|
|
1280
|
+
lastCounts = counts;
|
|
1281
|
+
return counts;
|
|
1282
|
+
};
|
|
1283
|
+
const withWaiting = (w, counts = lastCounts) => ({
|
|
1284
|
+
...w,
|
|
1285
|
+
waiting: counts.get(`${w.file}|${w.name}`) ?? 0
|
|
1286
|
+
});
|
|
1287
|
+
const asts = /* @__PURE__ */ new Map();
|
|
1288
|
+
async function astFor(file, name) {
|
|
1289
|
+
const key = `${file}|${name}`;
|
|
1290
|
+
let mtimeMs;
|
|
1291
|
+
try {
|
|
1292
|
+
mtimeMs = fs8.statSync(file).mtimeMs;
|
|
1293
|
+
} catch {
|
|
1294
|
+
return void 0;
|
|
1295
|
+
}
|
|
1296
|
+
const hit = asts.get(key);
|
|
1297
|
+
if (hit && hit.mtimeMs === mtimeMs) return hit.ast;
|
|
1298
|
+
const { ast } = await parseOne(file, name);
|
|
1299
|
+
asts.set(key, { mtimeMs, ast });
|
|
1300
|
+
return ast;
|
|
1301
|
+
}
|
|
1302
|
+
async function gateView(rec) {
|
|
1303
|
+
const g = rec.gate;
|
|
1304
|
+
if (!g) return void 0;
|
|
1305
|
+
const ast = await astFor(rec.filePath, rec.workflowName);
|
|
1306
|
+
const inst = ast?.instances.find((i) => i.id === g.node);
|
|
1307
|
+
const nt = ast && inst ? nodeTypeOf(ast, inst) : void 0;
|
|
1308
|
+
return {
|
|
1309
|
+
id: g.id,
|
|
1310
|
+
kind: g.kind,
|
|
1311
|
+
node: g.node,
|
|
1312
|
+
inputs: g.inputs,
|
|
1313
|
+
absent: g.absent,
|
|
1314
|
+
outputs: g.outputs,
|
|
1315
|
+
hasSuccessPort: g.hasSuccessPort,
|
|
1316
|
+
hasFailurePort: g.hasFailurePort,
|
|
1317
|
+
outputTypes: Object.fromEntries(g.outputs.map((o) => [o, nt?.outputs?.[o]?.tsType ?? "unknown"])),
|
|
1318
|
+
outputSchema: ast ? gateOutputSchemas(ast, g.node, rec.filePath) : null,
|
|
1319
|
+
// The words for whoever answers: the gate function's own description,
|
|
1320
|
+
// and each output's label from its @output line. Types alone say what
|
|
1321
|
+
// shape an answer has, not what it means.
|
|
1322
|
+
description: nt?.description ?? "",
|
|
1323
|
+
outputLabels: Object.fromEntries(g.outputs.map((o) => [o, nt?.outputs?.[o]?.label ?? ""])),
|
|
1324
|
+
inputLabels: Object.fromEntries(Object.keys(g.inputs).map((i) => [i, nt?.inputs?.[i]?.label ?? ""]))
|
|
1325
|
+
};
|
|
1326
|
+
}
|
|
1327
|
+
async function snapshot(id) {
|
|
1328
|
+
const d = debug.get(id);
|
|
1329
|
+
if (d) return debugSnapshot(d, debugSource.get(id));
|
|
1330
|
+
const l = live.get(id);
|
|
1331
|
+
const rec = await coordinator.record(id);
|
|
1332
|
+
if (l) return { id, file: l.file, name: l.name, params: l.params, mocks: l.mocks, source: l.source, status: l.status, startedAt: l.startedAt, updatedAt: Date.now(), error: l.error, traced: true, agent: rec?.agent, agents: rec?.agents, origin: rec?.origin ?? "console" };
|
|
1333
|
+
if (!rec) return void 0;
|
|
1334
|
+
return {
|
|
1335
|
+
id,
|
|
1336
|
+
file: rec.filePath,
|
|
1337
|
+
name: rec.workflowName,
|
|
1338
|
+
params: rec.params,
|
|
1339
|
+
mocks: rec.mocks,
|
|
1340
|
+
source: rec.source,
|
|
1341
|
+
status: rec.status,
|
|
1342
|
+
startedAt: at(rec.createdAt),
|
|
1343
|
+
updatedAt: at(rec.updatedAt),
|
|
1344
|
+
gate: rec.gate ? { node: rec.gate.node, kind: rec.gate.kind } : void 0,
|
|
1345
|
+
due: rec.due ? { at: at(rec.due.at), action: rec.due.action } : void 0,
|
|
1346
|
+
result: rec.result,
|
|
1347
|
+
error: rec.error,
|
|
1348
|
+
failedAt: rec.failedNode,
|
|
1349
|
+
traced: !!rec.traced,
|
|
1350
|
+
agent: rec.agent,
|
|
1351
|
+
agents: rec.agents,
|
|
1352
|
+
origin: rec.origin
|
|
1353
|
+
};
|
|
1354
|
+
}
|
|
1355
|
+
async function fullSnapshot(id) {
|
|
1356
|
+
const snap = await snapshot(id);
|
|
1357
|
+
const rec = live.has(id) ? void 0 : await coordinator.record(id);
|
|
1358
|
+
return snap && rec?.gate ? { ...snap, gate: await gateView(rec) } : snap;
|
|
1359
|
+
}
|
|
1360
|
+
async function pushRun(id) {
|
|
1361
|
+
if (!subs.get(id)?.size) return;
|
|
1362
|
+
const run = await fullSnapshot(id);
|
|
1363
|
+
if (run) push(id, { type: "run", run });
|
|
1364
|
+
}
|
|
1365
|
+
async function changed(id) {
|
|
1366
|
+
listed = void 0;
|
|
1367
|
+
if (id) await pushRun(id);
|
|
1368
|
+
broadcast({ type: "runs" });
|
|
1369
|
+
}
|
|
1370
|
+
async function drive(l, segment) {
|
|
1371
|
+
live.set(l.id, l);
|
|
1372
|
+
listed = void 0;
|
|
1373
|
+
broadcast({ type: "runs" });
|
|
1374
|
+
void pushRun(l.id);
|
|
1375
|
+
const onEvent = (ev) => {
|
|
1376
|
+
const entry = { t: ev.timestamp, e: ev.data ?? ev };
|
|
1377
|
+
l.events.push(entry);
|
|
1378
|
+
push(l.id, { type: "event", ...entry });
|
|
1379
|
+
};
|
|
1380
|
+
try {
|
|
1381
|
+
await segment(onEvent);
|
|
1382
|
+
live.delete(l.id);
|
|
1383
|
+
} catch (err) {
|
|
1384
|
+
if (await coordinator.record(l.id)) live.delete(l.id);
|
|
1385
|
+
else {
|
|
1386
|
+
l.status = "failed";
|
|
1387
|
+
l.error = getErrorMessage(err);
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
await changed(l.id);
|
|
1391
|
+
void afterSegment(l.id);
|
|
1392
|
+
}
|
|
1393
|
+
async function afterSegment(id, asked = false) {
|
|
1394
|
+
const rec = await coordinator.record(id);
|
|
1395
|
+
if (!rec || rec.status !== "waiting" || rec.gate?.kind !== "agent" || rec.agents === "manual" && !asked) return;
|
|
1396
|
+
const note = () => changed(id);
|
|
1397
|
+
try {
|
|
1398
|
+
if (asked && rec.agents === "manual") await coordinator.setAgent(id, rec.agent);
|
|
1399
|
+
const step = await answerAgentGate(coordinator, id, {
|
|
1400
|
+
projectDir: projectDir(),
|
|
1401
|
+
profiles: profiles(),
|
|
1402
|
+
outputSchema: async (r) => {
|
|
1403
|
+
const g = await gateView(r);
|
|
1404
|
+
return g ? { schema: g.outputSchema ?? null, types: g.outputTypes } : void 0;
|
|
1405
|
+
},
|
|
1406
|
+
onEvent: (e) => {
|
|
1407
|
+
const entry = { t: Date.now(), e };
|
|
1408
|
+
(agentEvents.get(id) ?? agentEvents.set(id, []).get(id)).push(entry);
|
|
1409
|
+
push(id, { ...e, t: entry.t });
|
|
1410
|
+
if (e.phase === "start" || e.phase === "done") void note();
|
|
1411
|
+
}
|
|
1412
|
+
});
|
|
1413
|
+
await note();
|
|
1414
|
+
if (step.kind === "answer") await resume(id, { answer: step.answer });
|
|
1415
|
+
else if (step.kind === "reject") await resume(id, { reject: step.reason });
|
|
1416
|
+
} catch (err) {
|
|
1417
|
+
await noteAnswerMisfit(coordinator, id, err);
|
|
1418
|
+
await note();
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
function startDebug(file, name, params, breakpoints, mocks, runTo, source) {
|
|
1422
|
+
const id = randomUUID3();
|
|
1423
|
+
const events = [];
|
|
1424
|
+
debugEvents.set(id, events);
|
|
1425
|
+
if (source) debugSource.set(id, source);
|
|
1426
|
+
void debug.start({
|
|
1427
|
+
id,
|
|
1428
|
+
file,
|
|
1429
|
+
name,
|
|
1430
|
+
params,
|
|
1431
|
+
breakpoints,
|
|
1432
|
+
mocks,
|
|
1433
|
+
onEvent: (ev) => {
|
|
1434
|
+
const entry = { t: ev.timestamp, e: ev.data ?? ev };
|
|
1435
|
+
events.push(entry);
|
|
1436
|
+
push(id, { type: "event", ...entry });
|
|
1437
|
+
},
|
|
1438
|
+
onChange: () => {
|
|
1439
|
+
void pushRun(id);
|
|
1440
|
+
broadcast({ type: "runs" });
|
|
1441
|
+
}
|
|
1442
|
+
}).then((view) => {
|
|
1443
|
+
if (runTo === "breakpoint" && view.status === "paused" && breakpoints.length) return debug.continue(id, true).then(() => void 0);
|
|
1444
|
+
}).catch(() => void 0);
|
|
1445
|
+
broadcast({ type: "runs" });
|
|
1446
|
+
return debugSnapshot(debug.get(id), debugSource.get(id));
|
|
1447
|
+
}
|
|
1448
|
+
async function start(file, name, params, mocks, source, agents) {
|
|
1449
|
+
const ast = await astFor(file, name);
|
|
1450
|
+
if (!ast) throw new Error((await parseOne(file, name)).errors.join("\n"));
|
|
1451
|
+
const id = randomUUID3();
|
|
1452
|
+
const l = { id, file, name, params, mocks, source, startedAt: Date.now(), status: "running", events: [], abort: new AbortController() };
|
|
1453
|
+
void drive(l, (onEvent) => coordinator.start({ filePath: file, workflowName: name, params, runId: id, mocks, source, agents, origin: "console" }, { onEvent, abortSignal: l.abort.signal }));
|
|
1454
|
+
return await snapshot(id);
|
|
1455
|
+
}
|
|
1456
|
+
async function resume(id, input) {
|
|
1457
|
+
if (live.has(id)) throw new Error("run is already resuming");
|
|
1458
|
+
const rec = await coordinator.record(id);
|
|
1459
|
+
if (rec?.gate && isAnswering(rec.agent, rec.gate.id)) throw new Error(`agent profile ${rec.agent.profile} is answering this gate. Wait for it, or cancel the run`);
|
|
1460
|
+
const resolve9 = "reject" in input ? { reject: input.reject ?? "" } : { answer: input.answer };
|
|
1461
|
+
await coordinator.checkResume({ runId: id, input: resolve9 });
|
|
1462
|
+
if (!rec) return;
|
|
1463
|
+
const l = { id, file: rec.filePath, name: rec.workflowName, params: rec.params, startedAt: at(rec.createdAt), status: "running", events: [], abort: new AbortController() };
|
|
1464
|
+
void drive(l, (onEvent) => coordinator.resume({ runId: id, input: resolve9 }, { onEvent, abortSignal: l.abort.signal }));
|
|
1465
|
+
}
|
|
1466
|
+
async function cancel(id) {
|
|
1467
|
+
if (debug.get(id)) {
|
|
1468
|
+
await debug.abort(id).catch(() => void 0);
|
|
1469
|
+
return;
|
|
1470
|
+
}
|
|
1471
|
+
const l = live.get(id);
|
|
1472
|
+
if (l?.status === "running") {
|
|
1473
|
+
l.abort.abort();
|
|
1474
|
+
return;
|
|
1475
|
+
}
|
|
1476
|
+
if ((await coordinator.record(id))?.status === "waiting") {
|
|
1477
|
+
await coordinator.cancel(id);
|
|
1478
|
+
await changed(id);
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
async function list(file, name) {
|
|
1482
|
+
const inFlight = [
|
|
1483
|
+
...debug.list().filter((d) => (!file || d.file === file) && (!name || d.name === name)).map((d) => debugSnapshot(d, debugSource.get(d.id))),
|
|
1484
|
+
...await Promise.all([...live.values()].filter((l) => (!file || l.file === file) && (!name || l.name === name)).map(async (l) => await snapshot(l.id)))
|
|
1485
|
+
];
|
|
1486
|
+
const stored = (file ? await coordinator.list({ filePath: file }) : await storeList()).filter((s) => (!name || s.workflowName === name) && !live.has(s.runId)).map((s) => ({
|
|
1487
|
+
id: s.runId,
|
|
1488
|
+
file: s.filePath,
|
|
1489
|
+
name: s.workflowName,
|
|
1490
|
+
status: s.status,
|
|
1491
|
+
params: s.params,
|
|
1492
|
+
mocks: s.mocks,
|
|
1493
|
+
source: s.source,
|
|
1494
|
+
startedAt: at(s.createdAt),
|
|
1495
|
+
updatedAt: at(s.updatedAt),
|
|
1496
|
+
gate: s.gate,
|
|
1497
|
+
failedAt: s.failedNode,
|
|
1498
|
+
origin: s.origin,
|
|
1499
|
+
due: s.due ? { at: at(s.due.at), action: s.due.action } : void 0
|
|
1500
|
+
}));
|
|
1501
|
+
return [...inFlight, ...stored].sort((a, b) => b.startedAt - a.startedAt).filter((r, i) => i < 20 || live.has(r.id) || debug.get(r.id));
|
|
1502
|
+
}
|
|
1503
|
+
async function remove(id) {
|
|
1504
|
+
if (live.has(id) || debug.get(id)) throw new Error("the run is in flight. Cancel it first");
|
|
1505
|
+
await coordinator.remove(id);
|
|
1506
|
+
await changed();
|
|
1507
|
+
}
|
|
1508
|
+
async function watch(id, req, res) {
|
|
1509
|
+
sse(res);
|
|
1510
|
+
send(res, { type: "run", run: await fullSnapshot(id) });
|
|
1511
|
+
const lines = [];
|
|
1512
|
+
for (const entry of [...await coordinator.trace(id), ...live.get(id)?.events ?? [], ...debugEvents.get(id) ?? []]) lines.push({ t: entry.t, line: JSON.stringify({ type: "event", ...entry }) });
|
|
1513
|
+
for (const entry of agentEvents.get(id) ?? []) lines.push({ t: entry.t, line: JSON.stringify({ ...entry.e, t: entry.t }) });
|
|
1514
|
+
lines.sort((a, b) => a.t - b.t);
|
|
1515
|
+
for (const l of lines) res.write(`data: ${l.line}
|
|
1516
|
+
|
|
1517
|
+
`);
|
|
1518
|
+
send(res, { type: "synced" });
|
|
1519
|
+
const set = subs.get(id) ?? subs.set(id, /* @__PURE__ */ new Set()).get(id);
|
|
1520
|
+
set.add(res);
|
|
1521
|
+
req.on("close", () => {
|
|
1522
|
+
set.delete(res);
|
|
1523
|
+
if (!set.size) subs.delete(id);
|
|
1524
|
+
});
|
|
1525
|
+
}
|
|
1526
|
+
async function withEvents(id) {
|
|
1527
|
+
return { ...await fullSnapshot(id), events: [...await coordinator.trace(id), ...live.get(id)?.events ?? []] };
|
|
1528
|
+
}
|
|
1529
|
+
async function askAgent(id) {
|
|
1530
|
+
const rec = await coordinator.record(id);
|
|
1531
|
+
if (!rec || rec.status !== "waiting" || rec.gate?.kind !== "agent") return "the run is not waiting at an agent gate";
|
|
1532
|
+
if (isAnswering(rec.agent, rec.gate.id)) return `${rec.agent.profile} is already answering`;
|
|
1533
|
+
void afterSegment(id, true);
|
|
1534
|
+
return void 0;
|
|
1535
|
+
}
|
|
1536
|
+
let storeWatcher;
|
|
1537
|
+
let storePoll;
|
|
1538
|
+
async function follow(opts) {
|
|
1539
|
+
if (opts.watching && opts.runsDir) {
|
|
1540
|
+
fs8.mkdirSync(opts.runsDir, { recursive: true });
|
|
1541
|
+
const chokidar = await import("chokidar");
|
|
1542
|
+
storeWatcher = chokidar.watch(opts.runsDir, { ignoreInitial: true, depth: 1 });
|
|
1543
|
+
storeWatcher.on("all", (_event, file) => {
|
|
1544
|
+
if (typeof file !== "string" || path7.basename(file) !== "run.json") return;
|
|
1545
|
+
void changed(path7.basename(path7.dirname(file)));
|
|
1546
|
+
});
|
|
1547
|
+
} else if (opts.watching) {
|
|
1548
|
+
let seen = /* @__PURE__ */ new Map();
|
|
1549
|
+
storePoll = setInterval(() => {
|
|
1550
|
+
void coordinator.list().then((rows) => {
|
|
1551
|
+
const now = new Map(rows.map((r) => [r.runId, r.updatedAt]));
|
|
1552
|
+
const moved = rows.filter((r) => seen.get(r.runId) !== r.updatedAt).map((r) => r.runId);
|
|
1553
|
+
const gone = [...seen.keys()].some((id) => !now.has(id));
|
|
1554
|
+
seen = now;
|
|
1555
|
+
if (!moved.length && !gone) return;
|
|
1556
|
+
listed = void 0;
|
|
1557
|
+
broadcast({ type: "runs" });
|
|
1558
|
+
for (const id of moved) if (!live.has(id)) void pushRun(id);
|
|
1559
|
+
}).catch(() => void 0);
|
|
1560
|
+
}, 3e3);
|
|
1561
|
+
storePoll.unref?.();
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
const clock = setInterval(() => {
|
|
1565
|
+
void coordinator.tick().then(async (moved) => {
|
|
1566
|
+
for (const run of [...moved.woke, ...moved.timedOut]) {
|
|
1567
|
+
await changed(run.runId);
|
|
1568
|
+
void afterSegment(run.runId);
|
|
1569
|
+
}
|
|
1570
|
+
}).catch(() => void 0);
|
|
1571
|
+
}, 3e3);
|
|
1572
|
+
clock.unref?.();
|
|
1573
|
+
return {
|
|
1574
|
+
debug,
|
|
1575
|
+
/** The stored record, as the coordinator has it. */
|
|
1576
|
+
record: (id) => coordinator.record(id),
|
|
1577
|
+
/** A document kept beside the run. */
|
|
1578
|
+
kept: (id, name) => coordinator.kept(id, name),
|
|
1579
|
+
snapshot,
|
|
1580
|
+
list,
|
|
1581
|
+
start,
|
|
1582
|
+
startDebug,
|
|
1583
|
+
resume,
|
|
1584
|
+
cancel,
|
|
1585
|
+
remove,
|
|
1586
|
+
watch,
|
|
1587
|
+
withEvents,
|
|
1588
|
+
askAgent,
|
|
1589
|
+
follow,
|
|
1590
|
+
waitingCounts,
|
|
1591
|
+
withWaiting,
|
|
1592
|
+
/** A value set in a paused session, told to the pages that read values from the trace. */
|
|
1593
|
+
announce: push,
|
|
1594
|
+
/** Keep the run streams open through proxies. */
|
|
1595
|
+
ping() {
|
|
1596
|
+
for (const set of subs.values()) for (const res of set) res.write(": ping\n\n");
|
|
1597
|
+
},
|
|
1598
|
+
async close() {
|
|
1599
|
+
clearInterval(clock);
|
|
1600
|
+
if (storePoll) clearInterval(storePoll);
|
|
1601
|
+
await storeWatcher?.close();
|
|
1602
|
+
for (const set of subs.values()) for (const res of set) res.end();
|
|
1603
|
+
}
|
|
1604
|
+
};
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
// src/console/routes-agents.ts
|
|
1608
|
+
import * as fs9 from "node:fs";
|
|
1609
|
+
function createProfileCache(projectDir) {
|
|
1610
|
+
let cache;
|
|
1611
|
+
const get = () => {
|
|
1612
|
+
const dir = projectDir();
|
|
1613
|
+
let mtime;
|
|
1614
|
+
try {
|
|
1615
|
+
mtime = fs9.statSync(agentsFile(dir)).mtimeMs;
|
|
1616
|
+
} catch {
|
|
1617
|
+
mtime = 0;
|
|
1618
|
+
}
|
|
1619
|
+
if (!cache || cache.dir !== dir || cache.mtime !== mtime) cache = { dir, mtime, profiles: loadAgentProfiles(dir) };
|
|
1620
|
+
return cache.profiles;
|
|
1621
|
+
};
|
|
1622
|
+
return {
|
|
1623
|
+
get,
|
|
1624
|
+
save(profiles) {
|
|
1625
|
+
saveAgentProfiles(projectDir(), profiles);
|
|
1626
|
+
cache = void 0;
|
|
1627
|
+
},
|
|
1628
|
+
describe() {
|
|
1629
|
+
const p = get();
|
|
1630
|
+
return {
|
|
1631
|
+
file: p.file,
|
|
1632
|
+
exists: p.exists,
|
|
1633
|
+
default: p.default ?? null,
|
|
1634
|
+
errors: p.errors,
|
|
1635
|
+
gates: p.gates,
|
|
1636
|
+
starter: STARTER_AGENTS_YAML,
|
|
1637
|
+
suggestedModels: SUGGESTED_MODELS,
|
|
1638
|
+
agents: Object.values(p.agents).map((a) => {
|
|
1639
|
+
const r = readiness(a);
|
|
1640
|
+
return { name: a.name, provider: a.provider, model: a.model || DEFAULT_MODEL[a.provider] || null, keyEnv: keyEnvOf(a) ?? null, ready: r.ready, reason: r.reason ?? null, description: a.description ?? null, system: a.system ?? null, maxIterations: a.maxIterations ?? null, baseUrl: a.baseUrl ?? null, bin: a.bin ?? null };
|
|
1641
|
+
})
|
|
1642
|
+
};
|
|
1643
|
+
}
|
|
1644
|
+
};
|
|
1645
|
+
}
|
|
1646
|
+
function profileFrom(name, b) {
|
|
1647
|
+
const str = (k) => typeof b[k] === "string" && b[k].trim() ? b[k].trim() : void 0;
|
|
1648
|
+
const num = (k) => typeof b[k] === "number" ? b[k] : typeof b[k] === "string" && b[k].trim() ? Number(b[k]) : void 0;
|
|
1649
|
+
return {
|
|
1650
|
+
name,
|
|
1651
|
+
provider: b.provider,
|
|
1652
|
+
model: str("model"),
|
|
1653
|
+
apiKeyEnv: str("apiKeyEnv"),
|
|
1654
|
+
baseUrl: str("baseUrl"),
|
|
1655
|
+
system: typeof b.system === "string" && b.system.trim() ? b.system : void 0,
|
|
1656
|
+
maxIterations: num("maxIterations"),
|
|
1657
|
+
maxTokens: num("maxTokens"),
|
|
1658
|
+
bin: str("bin"),
|
|
1659
|
+
description: str("description")
|
|
1660
|
+
};
|
|
1661
|
+
}
|
|
1662
|
+
var PROFILE = /^\/api\/agents\/profiles\/([^/]+)$/;
|
|
1663
|
+
var TRY = /^\/api\/agents\/try\/([^/]+)$/;
|
|
1664
|
+
var nameIn = ({ match }) => decodeURIComponent(match[1] ?? "");
|
|
1665
|
+
function agentRoutes(ctx) {
|
|
1666
|
+
const { profiles } = ctx;
|
|
1667
|
+
const describe = (call) => json(call.res, 200, profiles.describe());
|
|
1668
|
+
return [
|
|
1669
|
+
{ path: "/api/agents", handle: describe },
|
|
1670
|
+
// Whether an environment variable is set here -- never its value.
|
|
1671
|
+
{
|
|
1672
|
+
path: "/api/agents/env",
|
|
1673
|
+
handle: ({ res, q }) => json(res, 200, { name: q("name"), set: /^[A-Z_][A-Z0-9_]*$/.test(q("name")) && !!process.env[q("name")] })
|
|
1674
|
+
},
|
|
1675
|
+
{
|
|
1676
|
+
method: "PUT",
|
|
1677
|
+
path: "/api/agents/default",
|
|
1678
|
+
handle: async (call) => {
|
|
1679
|
+
const b = await call.body();
|
|
1680
|
+
const p = profiles.get();
|
|
1681
|
+
const name = typeof b.name === "string" && b.name ? b.name : void 0;
|
|
1682
|
+
if (name && !p.agents[name]) return json(call.res, 400, { error: `no profile named ${name}` });
|
|
1683
|
+
profiles.save({ agents: p.agents, default: name, gates: p.gates });
|
|
1684
|
+
return describe(call);
|
|
1685
|
+
}
|
|
1686
|
+
},
|
|
1687
|
+
{
|
|
1688
|
+
method: "PUT",
|
|
1689
|
+
path: "/api/agents/gates",
|
|
1690
|
+
handle: async (call) => {
|
|
1691
|
+
const b = await call.body();
|
|
1692
|
+
const p = profiles.get();
|
|
1693
|
+
const key = typeof b.key === "string" ? b.key.trim() : "";
|
|
1694
|
+
if (!key) return json(call.res, 400, { error: "a gate key is an agentId or workflow/node" });
|
|
1695
|
+
const gates = { ...p.gates };
|
|
1696
|
+
if (typeof b.profile === "string" && b.profile) {
|
|
1697
|
+
if (!p.agents[b.profile]) return json(call.res, 400, { error: `no profile named ${b.profile}` });
|
|
1698
|
+
gates[key] = b.profile;
|
|
1699
|
+
} else delete gates[key];
|
|
1700
|
+
profiles.save({ agents: p.agents, default: p.default, gates });
|
|
1701
|
+
return describe(call);
|
|
1702
|
+
}
|
|
1703
|
+
},
|
|
1704
|
+
{
|
|
1705
|
+
method: "POST",
|
|
1706
|
+
path: TRY,
|
|
1707
|
+
handle: async (call) => {
|
|
1708
|
+
const profile = profiles.get().agents[nameIn(call)];
|
|
1709
|
+
if (!profile) return json(call.res, 404, { error: `no profile named ${nameIn(call)}` });
|
|
1710
|
+
return json(call.res, 200, await tryProfile(profile, process.env, { cwd: ctx.projectDir() }));
|
|
1711
|
+
}
|
|
1712
|
+
},
|
|
1713
|
+
{
|
|
1714
|
+
method: "PUT",
|
|
1715
|
+
path: PROFILE,
|
|
1716
|
+
handle: async (call) => {
|
|
1717
|
+
const name = nameIn(call);
|
|
1718
|
+
const p = profiles.get();
|
|
1719
|
+
const profile = profileFrom(name, await call.body());
|
|
1720
|
+
const problems = validateProfile(profile);
|
|
1721
|
+
if (problems.length) return json(call.res, 400, { error: problems.join("; ") });
|
|
1722
|
+
const agents = { ...p.agents, [name]: profile };
|
|
1723
|
+
const def = p.default && agents[p.default] ? p.default : Object.keys(agents).length === 1 ? name : p.default;
|
|
1724
|
+
profiles.save({ agents, default: def, gates: p.gates });
|
|
1725
|
+
return describe(call);
|
|
1726
|
+
}
|
|
1727
|
+
},
|
|
1728
|
+
{
|
|
1729
|
+
method: "DELETE",
|
|
1730
|
+
path: PROFILE,
|
|
1731
|
+
handle: (call) => {
|
|
1732
|
+
const name = nameIn(call);
|
|
1733
|
+
const p = profiles.get();
|
|
1734
|
+
if (!p.agents[name]) return json(call.res, 404, { error: `no profile named ${name}` });
|
|
1735
|
+
const agents = { ...p.agents };
|
|
1736
|
+
delete agents[name];
|
|
1737
|
+
const gates = Object.fromEntries(Object.entries(p.gates).filter(([, v]) => v !== name));
|
|
1738
|
+
profiles.save({ agents, default: p.default === name ? void 0 : p.default, gates });
|
|
1739
|
+
return describe(call);
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1742
|
+
];
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
// src/console/routes-project.ts
|
|
1746
|
+
import * as fs10 from "node:fs";
|
|
1747
|
+
import * as path8 from "node:path";
|
|
1748
|
+
|
|
1749
|
+
// src/console/diff-view.ts
|
|
1750
|
+
var CONTROL4 = /* @__PURE__ */ new Set(["execute", "onSuccess", "onFailure"]);
|
|
1751
|
+
var isControl = (c) => CONTROL4.has(c.from.port) || c.to.port === "execute";
|
|
1752
|
+
function typeOf(ast, inst) {
|
|
1753
|
+
return ast.nodeTypes.find((n) => n.name === inst.nodeType) ?? ast.nodeTypes.find((n) => n.functionName === inst.nodeType);
|
|
1754
|
+
}
|
|
1755
|
+
var portList = (map) => Object.entries(map ?? {}).filter(([k, p]) => !CONTROL4.has(k) && !p.isControlFlow).map(([k, p]) => ({ name: k, tsType: p.tsType ?? String(p.dataType).toLowerCase(), optional: !!p.optional, description: p.description ?? "" }));
|
|
1756
|
+
function nodeOf(ast, inst) {
|
|
1757
|
+
const nt = typeOf(ast, inst);
|
|
1758
|
+
return {
|
|
1759
|
+
id: inst.id,
|
|
1760
|
+
type: inst.nodeType,
|
|
1761
|
+
label: stepLabel(inst, nt),
|
|
1762
|
+
description: nt?.description ?? "",
|
|
1763
|
+
builtin: !nt?.functionText,
|
|
1764
|
+
color: inst.config?.color ?? nt?.visuals?.color ?? null,
|
|
1765
|
+
icon: inst.config?.icon ?? nt?.visuals?.icon ?? null,
|
|
1766
|
+
pull: inst.config?.pullExecution !== void 0,
|
|
1767
|
+
gate: nt?.durableGate ?? null,
|
|
1768
|
+
expression: !!nt?.expression,
|
|
1769
|
+
durablePure: !!nt?.durablePure,
|
|
1770
|
+
effect: !!nt?.durableEffect,
|
|
1771
|
+
async: !!nt?.isAsync,
|
|
1772
|
+
inputs: portList(nt?.inputs),
|
|
1773
|
+
outputs: portList(nt?.outputs),
|
|
1774
|
+
source: nt?.functionText ?? "",
|
|
1775
|
+
file: nt?.sourceLocation?.file ?? ast.sourceFile,
|
|
1776
|
+
line: nt?.sourceLocation?.line ?? null,
|
|
1777
|
+
outputSchema: null,
|
|
1778
|
+
expr: (inst.config?.portConfigs ?? []).filter((c) => c.expression).map((c) => ({ port: c.portName, expr: String(c.expression) })),
|
|
1779
|
+
pack: null,
|
|
1780
|
+
deploy: null
|
|
1781
|
+
};
|
|
1782
|
+
}
|
|
1783
|
+
function instanceDetail(d, typeChanged) {
|
|
1784
|
+
const bits = [];
|
|
1785
|
+
if (d.changes.nodeType) bits.push(`now ${d.changes.nodeType.after}, was ${d.changes.nodeType.before}`);
|
|
1786
|
+
if (d.changes.config?.portConfigs) bits.push("expressions changed");
|
|
1787
|
+
if (d.changes.config?.pullExecution) bits.push(d.changes.config.pullExecution.after ? "now pulled on demand" : "no longer pulled");
|
|
1788
|
+
if (d.changes.parent) {
|
|
1789
|
+
const a = d.changes.parent.after, b = d.changes.parent.before;
|
|
1790
|
+
bits.push(a ? `moved into ${a.id} (${a.scope})` : b ? `moved out of ${b.id} (${b.scope})` : "scope changed");
|
|
1791
|
+
}
|
|
1792
|
+
if (typeChanged) bits.push(typeDetail(typeChanged));
|
|
1793
|
+
return bits.join(", ");
|
|
1794
|
+
}
|
|
1795
|
+
function typeDetail(d) {
|
|
1796
|
+
const bits = [];
|
|
1797
|
+
const io = (list, word) => {
|
|
1798
|
+
for (const p of list ?? []) bits.push(`${p.type === "ADDED" ? "+" : p.type === "REMOVED" ? "\u2212" : "~"}${word} ${p.portName}`);
|
|
1799
|
+
};
|
|
1800
|
+
io(d.changes.inputs, "in");
|
|
1801
|
+
io(d.changes.outputs, "out");
|
|
1802
|
+
if (d.changes.executeWhen) bits.push("execute-when changed");
|
|
1803
|
+
if (d.changes.isAsync) bits.push(d.changes.isAsync.after ? "now async" : "no longer async");
|
|
1804
|
+
if (d.changes.scope) bits.push("scope changed");
|
|
1805
|
+
if (d.changes.functionName) bits.push(`function ${d.changes.functionName.after}`);
|
|
1806
|
+
return bits.join(", ");
|
|
1807
|
+
}
|
|
1808
|
+
function buildDiffView(before, after) {
|
|
1809
|
+
const diff = WorkflowDiffer.compare(before, after);
|
|
1810
|
+
const reasons = diff.identical ? [] : getImpactReasons(diff);
|
|
1811
|
+
const beforeInst = new Map(before.instances.map((i) => [i.id, i]));
|
|
1812
|
+
const afterInst = new Map(after.instances.map((i) => [i.id, i]));
|
|
1813
|
+
const label = (id) => {
|
|
1814
|
+
if (id === "Start" || id === "Exit") return id;
|
|
1815
|
+
const inst = afterInst.get(id) ?? beforeInst.get(id);
|
|
1816
|
+
return inst ? stepLabel(inst, typeOf(afterInst.has(id) ? after : before, inst)) : id;
|
|
1817
|
+
};
|
|
1818
|
+
const typeDiffs = new Map(diff.nodeTypes.filter((n) => n.changeType === "MODIFIED").map((n) => [n.name, n]));
|
|
1819
|
+
const steps = [];
|
|
1820
|
+
for (const d of diff.instances) {
|
|
1821
|
+
const inst = afterInst.get(d.id) ?? beforeInst.get(d.id);
|
|
1822
|
+
if (!inst) continue;
|
|
1823
|
+
if (d.changeType === "ADDED") steps.push({ id: d.id, label: label(d.id), change: "added", detail: `${inst.nodeType}` });
|
|
1824
|
+
else if (d.changeType === "REMOVED") steps.push({ id: d.id, label: label(d.id), change: "removed", detail: `${inst.nodeType}` });
|
|
1825
|
+
else {
|
|
1826
|
+
const t = typeDiffs.get(inst.nodeType) ?? [...typeDiffs.values()].find((x) => typeOf(after, inst)?.functionName === x.name);
|
|
1827
|
+
const detail = instanceDetail(d, t);
|
|
1828
|
+
if (detail) steps.push({ id: d.id, label: label(d.id), change: "changed", detail });
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
const touched = new Set(steps.map((s) => s.id));
|
|
1832
|
+
for (const inst of after.instances) {
|
|
1833
|
+
if (touched.has(inst.id)) continue;
|
|
1834
|
+
const nt = typeOf(after, inst);
|
|
1835
|
+
const t = typeDiffs.get(inst.nodeType) ?? (nt ? typeDiffs.get(nt.name) : void 0);
|
|
1836
|
+
if (t) {
|
|
1837
|
+
steps.push({ id: inst.id, label: label(inst.id), change: "changed", detail: typeDetail(t) });
|
|
1838
|
+
touched.add(inst.id);
|
|
1839
|
+
}
|
|
1840
|
+
}
|
|
1841
|
+
const wiring = [];
|
|
1842
|
+
const edgesAdded = /* @__PURE__ */ new Map();
|
|
1843
|
+
const edgesRemoved = /* @__PURE__ */ new Map();
|
|
1844
|
+
for (const c of diff.connections) {
|
|
1845
|
+
const change = c.changeType === "ADDED" ? "added" : "removed";
|
|
1846
|
+
const kind = isControl(c) ? c.from.port === "onFailure" ? "fail" : "ok" : "data";
|
|
1847
|
+
const text = kind === "data" ? `${label(c.from.node)}.${c.from.port} \u2192 ${label(c.to.node)}.${c.to.port}` : `${label(c.from.node)}${kind === "fail" ? " on failure" : ""} \u2192 ${label(c.to.node)}`;
|
|
1848
|
+
wiring.push({ change, from: c.from.node, to: c.to.node, fromPort: c.from.port, toPort: c.to.port, kind, label: text });
|
|
1849
|
+
if (kind !== "data") (change === "added" ? edgesAdded : edgesRemoved).set(`${c.from.node}>${c.to.node}`, [c.from.node, c.to.node]);
|
|
1850
|
+
}
|
|
1851
|
+
const contract = [];
|
|
1852
|
+
const ports3 = (side, p) => {
|
|
1853
|
+
for (const x of p.added) contract.push({ side, name: x.name, change: "added", detail: x.definition.tsType ?? String(x.definition.dataType ?? "") });
|
|
1854
|
+
for (const x of p.removed) contract.push({ side, name: x.name, change: "removed", detail: x.definition.tsType ?? String(x.definition.dataType ?? "") });
|
|
1855
|
+
for (const x of p.modified) contract.push({ side, name: x.name, change: "changed", detail: `${x.before.tsType ?? x.before.dataType} \u2192 ${x.after.tsType ?? x.after.dataType}` });
|
|
1856
|
+
};
|
|
1857
|
+
ports3("in", diff.startPorts);
|
|
1858
|
+
ports3("out", diff.exitPorts);
|
|
1859
|
+
const removedInstances = before.instances.filter((i) => !afterInst.has(i.id));
|
|
1860
|
+
const afterTypes = new Set(after.nodeTypes.map((n) => n.name));
|
|
1861
|
+
const unionTypes = [...after.nodeTypes, ...before.nodeTypes.filter((n) => !afterTypes.has(n.name))];
|
|
1862
|
+
const unionIds = new Set([...after.instances, ...removedInstances].map((i) => i.id));
|
|
1863
|
+
const afterConn = new Set(after.connections.map(connKey));
|
|
1864
|
+
const removedConnections = before.connections.filter((c) => !afterConn.has(connKey(c)) && (c.from.node === "Start" || unionIds.has(c.from.node)) && (c.to.node === "Exit" || unionIds.has(c.to.node)));
|
|
1865
|
+
const union = { ...after, nodeTypes: unionTypes, instances: [...after.instances, ...removedInstances], connections: [...after.connections, ...removedConnections] };
|
|
1866
|
+
let model;
|
|
1867
|
+
try {
|
|
1868
|
+
model = buildProcessModel(union);
|
|
1869
|
+
} catch {
|
|
1870
|
+
model = null;
|
|
1871
|
+
}
|
|
1872
|
+
if (model) {
|
|
1873
|
+
const unionInst = new Map(union.instances.map((i) => [i.id, i]));
|
|
1874
|
+
const stampAll = (steps2) => steps2.forEach((s) => {
|
|
1875
|
+
const inst = unionInst.get(s.id);
|
|
1876
|
+
if (inst) {
|
|
1877
|
+
s.label = label(s.id);
|
|
1878
|
+
s.pull = inst.config?.pullExecution !== void 0;
|
|
1879
|
+
}
|
|
1880
|
+
stampAll(s.children);
|
|
1881
|
+
});
|
|
1882
|
+
stampAll(model.steps);
|
|
1883
|
+
}
|
|
1884
|
+
const nodes = {};
|
|
1885
|
+
for (const inst of removedInstances) nodes[inst.id] = nodeOf(before, inst);
|
|
1886
|
+
return {
|
|
1887
|
+
identical: diff.identical,
|
|
1888
|
+
impact: diff.impact,
|
|
1889
|
+
reasons,
|
|
1890
|
+
steps,
|
|
1891
|
+
wiring,
|
|
1892
|
+
contract,
|
|
1893
|
+
marks: {
|
|
1894
|
+
added: steps.filter((s) => s.change === "added").map((s) => s.id),
|
|
1895
|
+
removed: steps.filter((s) => s.change === "removed").map((s) => s.id),
|
|
1896
|
+
changed: steps.filter((s) => s.change === "changed").map((s) => s.id),
|
|
1897
|
+
edgesAdded: [...edgesAdded.values()],
|
|
1898
|
+
edgesRemoved: [...edgesRemoved.values()]
|
|
1899
|
+
},
|
|
1900
|
+
model,
|
|
1901
|
+
nodes
|
|
1902
|
+
};
|
|
1903
|
+
}
|
|
1904
|
+
var connKey = (c) => `${c.from.node}.${c.from.port}${c.from.scope ? ":" + c.from.scope : ""}>${c.to.node}.${c.to.port}${c.to.scope ? ":" + c.to.scope : ""}`;
|
|
1905
|
+
|
|
1906
|
+
// src/console/routes-services.ts
|
|
1907
|
+
function describeServe(supervisor) {
|
|
1908
|
+
const s = supervisor.view("serve");
|
|
1909
|
+
return {
|
|
1910
|
+
running: s.state === "running" && s.pid ? { url: s.url ?? null, pid: s.pid, startedAt: s.startedAt, version: s.activity?.version ?? VERSION, install: s.activity?.install ?? "", owned: s.owned, token: s.token ?? null } : null,
|
|
1911
|
+
state: s.state,
|
|
1912
|
+
command: "fw serve --trace"
|
|
1913
|
+
};
|
|
1914
|
+
}
|
|
1915
|
+
function serviceRoutes(ctx) {
|
|
1916
|
+
const listing = () => ({ services: ctx.supervisor().list(), settings: ctx.supervisor().settings() });
|
|
1917
|
+
return [
|
|
1918
|
+
{ path: "/api/serve", handle: ({ res }) => json(res, 200, describeServe(ctx.supervisor())) },
|
|
1919
|
+
// The project's services: what runs, its settings, start and stop, its output.
|
|
1920
|
+
{ path: "/api/services", handle: ({ res }) => json(res, 200, listing()) },
|
|
1921
|
+
{
|
|
1922
|
+
path: /^\/api\/services\/(serve|watch)\/(start|stop|restart|settings|logs)$/,
|
|
1923
|
+
handle: async ({ req, res, q, body, match }) => {
|
|
1924
|
+
const kind = match[1];
|
|
1925
|
+
const action = match[2];
|
|
1926
|
+
const supervisor = ctx.supervisor();
|
|
1927
|
+
try {
|
|
1928
|
+
if (action === "logs") {
|
|
1929
|
+
if (q("format") === "json") return json(res, 200, supervisor.logs(kind));
|
|
1930
|
+
sse(res);
|
|
1931
|
+
for (const line of supervisor.logs(kind)) send(res, line);
|
|
1932
|
+
send(res, { synced: true });
|
|
1933
|
+
const off = supervisor.onLog(kind, (line) => send(res, line));
|
|
1934
|
+
req.on("close", off);
|
|
1935
|
+
return;
|
|
1936
|
+
}
|
|
1937
|
+
if (req.method !== "POST" && req.method !== "PUT") return json(res, 405, { error: "POST to start, stop or restart; PUT settings" });
|
|
1938
|
+
const b = await body();
|
|
1939
|
+
if (action === "settings") {
|
|
1940
|
+
supervisor.saveSettings(kind, b);
|
|
1941
|
+
return json(res, 200, listing());
|
|
1942
|
+
}
|
|
1943
|
+
if (action === "start") {
|
|
1944
|
+
if (Object.keys(b).length) supervisor.saveSettings(kind, b);
|
|
1945
|
+
supervisor.start(kind);
|
|
1946
|
+
}
|
|
1947
|
+
if (action === "stop") await supervisor.stop(kind, typeof b.pid === "number" ? b.pid : void 0);
|
|
1948
|
+
if (action === "restart") await supervisor.restart(kind);
|
|
1949
|
+
return json(res, 200, listing());
|
|
1950
|
+
} catch (err) {
|
|
1951
|
+
return json(res, 400, { error: getErrorMessage(err) });
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
},
|
|
1955
|
+
{
|
|
1956
|
+
path: "/api/events",
|
|
1957
|
+
handle: ({ req, res }) => {
|
|
1958
|
+
sse(res);
|
|
1959
|
+
ctx.followers.add(res);
|
|
1960
|
+
req.on("close", () => ctx.followers.delete(res));
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1963
|
+
];
|
|
1964
|
+
}
|
|
1965
|
+
|
|
1966
|
+
// src/console/routes-project.ts
|
|
1967
|
+
var OUTSIDE = { error: "file is outside the project" };
|
|
1968
|
+
function projectRoutes(ctx) {
|
|
1969
|
+
const here = () => {
|
|
1970
|
+
const dir = ctx.projectDir();
|
|
1971
|
+
return { dir, name: path8.basename(dir), parent: path8.dirname(dir) };
|
|
1972
|
+
};
|
|
1973
|
+
const { pageDir, scriptDir } = ctx.client;
|
|
1974
|
+
return [
|
|
1975
|
+
{ path: "/", handle: ({ res }) => sendFile(res, path8.join(pageDir, "index.html"), "text/html") },
|
|
1976
|
+
{ path: "/app.js", handle: ({ res }) => sendFile(res, path8.join(scriptDir, "app.js"), "text/javascript") },
|
|
1977
|
+
{ path: "/styles.css", handle: ({ res }) => sendFile(res, path8.join(pageDir, "styles.css"), "text/css") },
|
|
1978
|
+
{ path: "/synergenius.svg", handle: ({ res }) => sendFile(res, path8.join(pageDir, "assets", "synergenius.svg"), "image/svg+xml") },
|
|
1979
|
+
{ method: "GET", path: "/api/project", handle: ({ res }) => json(res, 200, here()) },
|
|
1980
|
+
// Opening another project. The console is a local tool driving a local
|
|
1981
|
+
// engine, so any directory the user can read is fair game -- but it
|
|
1982
|
+
// must exist, and the answer re-points the watcher and the containment
|
|
1983
|
+
// check together so neither is left pointing at the old project.
|
|
1984
|
+
{
|
|
1985
|
+
method: "POST",
|
|
1986
|
+
path: "/api/project",
|
|
1987
|
+
handle: async ({ res, body }) => {
|
|
1988
|
+
const target = path8.resolve(String((await body()).dir ?? ""));
|
|
1989
|
+
if (!fs10.existsSync(target) || !fs10.statSync(target).isDirectory()) {
|
|
1990
|
+
return json(res, 400, { error: `not a directory: ${target}` });
|
|
1991
|
+
}
|
|
1992
|
+
await ctx.openProject(target);
|
|
1993
|
+
return json(res, 200, here());
|
|
1994
|
+
}
|
|
1995
|
+
},
|
|
1996
|
+
// Directories to choose from, so the console can offer a picker
|
|
1997
|
+
// rather than asking someone to type a path.
|
|
1998
|
+
{
|
|
1999
|
+
path: "/api/browse",
|
|
2000
|
+
handle: ({ res, q }) => {
|
|
2001
|
+
const at2 = path8.resolve(q("dir") || ctx.projectDir());
|
|
2002
|
+
try {
|
|
2003
|
+
const entries = fs10.readdirSync(at2, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules").map((e) => ({ name: e.name, dir: path8.join(at2, e.name) })).sort((a, b) => a.name.localeCompare(b.name));
|
|
2004
|
+
return json(res, 200, { dir: at2, parent: path8.dirname(at2), entries, crumbs: crumbsFor(at2) });
|
|
2005
|
+
} catch (err) {
|
|
2006
|
+
return json(res, 400, { error: getErrorMessage(err) });
|
|
2007
|
+
}
|
|
2008
|
+
}
|
|
2009
|
+
},
|
|
2010
|
+
{
|
|
2011
|
+
path: "/api/workflows",
|
|
2012
|
+
handle: async ({ res }) => {
|
|
2013
|
+
const list = scanWorkflowNames(ctx.projectDir());
|
|
2014
|
+
ctx.queueCheck();
|
|
2015
|
+
const counts = await ctx.runs.waitingCounts();
|
|
2016
|
+
return json(res, 200, list.map((w) => ctx.runs.withWaiting(w, counts)));
|
|
2017
|
+
}
|
|
2018
|
+
},
|
|
2019
|
+
// A workflow as something to hand to someone: a brief for people who
|
|
2020
|
+
// will not open the code, or the diagram. Sent as a download,
|
|
2021
|
+
// self-contained.
|
|
2022
|
+
{
|
|
2023
|
+
path: "/api/artifact",
|
|
2024
|
+
handle: async ({ res, q }) => {
|
|
2025
|
+
const target = ctx.inProject(q("file"));
|
|
2026
|
+
if (!target) return json(res, 400, OUTSIDE);
|
|
2027
|
+
const { ast, errors } = await parseOne(target, q("name"));
|
|
2028
|
+
if (!ast) return json(res, 400, { error: errors.join("\n") || "does not parse" });
|
|
2029
|
+
const theme = q("theme") === "dark" ? "dark" : "light";
|
|
2030
|
+
const kind = q("kind");
|
|
2031
|
+
if (!ARTIFACT_KINDS.includes(kind)) return json(res, 400, { error: `unknown artifact ${kind}` });
|
|
2032
|
+
let artifact;
|
|
2033
|
+
try {
|
|
2034
|
+
artifact = await renderArtifact(ast, kind, { theme, subtitle: path8.basename(ctx.projectDir()) });
|
|
2035
|
+
} catch (e) {
|
|
2036
|
+
return json(res, 500, { error: getErrorMessage(e) });
|
|
2037
|
+
}
|
|
2038
|
+
res.writeHead(200, { "Content-Type": artifact.type, "Content-Disposition": `attachment; filename="${ast.functionName}${artifact.extension}"`, "Cache-Control": "no-cache" });
|
|
2039
|
+
return res.end(artifact.body);
|
|
2040
|
+
}
|
|
2041
|
+
},
|
|
2042
|
+
{
|
|
2043
|
+
path: "/api/workflow",
|
|
2044
|
+
handle: async ({ res, q }) => {
|
|
2045
|
+
const file = ctx.inProject(q("file"));
|
|
2046
|
+
if (!file) return json(res, 400, OUTSIDE);
|
|
2047
|
+
return json(res, 200, await describeWorkflow(ctx.projectDir(), file, q("name")));
|
|
2048
|
+
}
|
|
2049
|
+
},
|
|
2050
|
+
// Expose a workflow as an endpoint, or change its routes: the @http
|
|
2051
|
+
// lines are rewritten, nothing else in the file is touched.
|
|
2052
|
+
{
|
|
2053
|
+
method: "PUT",
|
|
2054
|
+
path: "/api/workflow/http",
|
|
2055
|
+
handle: async ({ res, body }) => {
|
|
2056
|
+
const b = await body();
|
|
2057
|
+
const target = ctx.inProject(String(b.file ?? ""));
|
|
2058
|
+
if (!target) return json(res, 400, OUTSIDE);
|
|
2059
|
+
const routes = [];
|
|
2060
|
+
for (const raw of Array.isArray(b.routes) ? b.routes : []) {
|
|
2061
|
+
const r = routeFrom(raw);
|
|
2062
|
+
if (typeof r === "string") return json(res, 400, { error: r });
|
|
2063
|
+
if (routes.some((x) => x.method === r.method && x.path === r.path)) return json(res, 400, { error: `${r.method} ${r.path} is listed twice` });
|
|
2064
|
+
routes.push(r);
|
|
2065
|
+
}
|
|
2066
|
+
const out = await setHttpRoutes(target, String(b.name ?? ""), routes);
|
|
2067
|
+
if (!out.ok) return json(res, 400, { error: out.error });
|
|
2068
|
+
return json(res, 200, await describeWorkflow(ctx.projectDir(), target, String(b.name)));
|
|
2069
|
+
}
|
|
2070
|
+
},
|
|
2071
|
+
// Every route the project declares, as one list: what `fw serve` or
|
|
2072
|
+
// an embedding would mount, and what it would refuse.
|
|
2073
|
+
{
|
|
2074
|
+
path: "/api/endpoints",
|
|
2075
|
+
handle: async ({ res }) => {
|
|
2076
|
+
const names2 = scanWorkflowNames(ctx.projectDir());
|
|
2077
|
+
const parsed = await Promise.all(names2.map(async (w) => {
|
|
2078
|
+
const p = await parseOne(w.file, w.name);
|
|
2079
|
+
const ast = p.ast;
|
|
2080
|
+
const gates = ast ? ast.instances.filter((i) => nodeTypeOf(ast, i)?.durableGate !== void 0).length : 0;
|
|
2081
|
+
return { file: w.file, rel: w.rel, name: w.name, description: ast?.description ?? "", routes: ast?.options?.http ?? [], gates, params: ast ? ports2(ast.startPorts) : [], returns: ast ? ports2(ast.exitPorts) : [], parses: !!ast };
|
|
2082
|
+
}));
|
|
2083
|
+
const plan = planRoutes(parsed.map((w) => ({ name: w.name, routes: w.routes, w })));
|
|
2084
|
+
return json(res, 200, {
|
|
2085
|
+
workflows: parsed.filter((w) => w.routes.length).map(({ routes, ...w }) => ({ ...w, routes: routes.map((r) => ({ ...r, mounted: plan.mounted.some((m) => m.owner.name === w.name && m.route === r) })) })),
|
|
2086
|
+
candidates: parsed.filter((w) => !w.routes.length && w.parses).map(({ routes: _r, ...w }) => w),
|
|
2087
|
+
problems: plan.problems,
|
|
2088
|
+
serve: describeServe(ctx.supervisor())
|
|
2089
|
+
});
|
|
2090
|
+
}
|
|
2091
|
+
},
|
|
2092
|
+
// Version control: the commits that touched a file, and two versions
|
|
2093
|
+
// of a workflow as one marked picture. `from`/`to` are git refs, or
|
|
2094
|
+
// `worktree` for the file as it is now.
|
|
2095
|
+
{
|
|
2096
|
+
path: "/api/git/history",
|
|
2097
|
+
handle: async ({ res, q }) => {
|
|
2098
|
+
const file = ctx.inProject(q("file"));
|
|
2099
|
+
if (!file) return json(res, 400, OUTSIDE);
|
|
2100
|
+
return json(res, 200, await fileHistory(file));
|
|
2101
|
+
}
|
|
2102
|
+
},
|
|
2103
|
+
{
|
|
2104
|
+
path: "/api/diff",
|
|
2105
|
+
handle: async ({ res, q }) => {
|
|
2106
|
+
const file = ctx.inProject(q("file"));
|
|
2107
|
+
if (!file) return json(res, 400, OUTSIDE);
|
|
2108
|
+
const name = q("name"), from = q("from") || "HEAD", to = q("to") || "worktree";
|
|
2109
|
+
const [a, b] = await Promise.all([astAt(file, name, from), astAt(file, name, to)]);
|
|
2110
|
+
if (!a.ast) return json(res, 400, { error: a.error });
|
|
2111
|
+
if (!b.ast) return json(res, 400, { error: b.error });
|
|
2112
|
+
return json(res, 200, { from, to, ...buildDiffView(a.ast, b.ast) });
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
];
|
|
2116
|
+
}
|
|
2117
|
+
|
|
2118
|
+
// src/console/routes-tools.ts
|
|
2119
|
+
import * as path12 from "node:path";
|
|
2120
|
+
|
|
2121
|
+
// src/docs/guide.ts
|
|
2122
|
+
var GROUPS = [
|
|
2123
|
+
{ title: "Getting started", topics: [["orientation"], ["tutorial"], ["console"]] },
|
|
2124
|
+
{
|
|
2125
|
+
title: "Authoring",
|
|
2126
|
+
topics: [
|
|
2127
|
+
["concepts"],
|
|
2128
|
+
["jsdoc-grammar"],
|
|
2129
|
+
["advanced-annotations", { uses: ["pull", "expr", "scope"] }],
|
|
2130
|
+
["export-interface", { uses: ["scope"] }],
|
|
2131
|
+
["built-in-nodes", { uses: ["builtin:"] }],
|
|
2132
|
+
["node-conversion"],
|
|
2133
|
+
["visual-reference"],
|
|
2134
|
+
["scaffold"]
|
|
2135
|
+
]
|
|
2136
|
+
},
|
|
2137
|
+
{
|
|
2138
|
+
title: "Running",
|
|
2139
|
+
topics: [
|
|
2140
|
+
["library"],
|
|
2141
|
+
["durable-gates", { uses: ["gate:", "effect", "pure"] }],
|
|
2142
|
+
["debugging", { codes: true }],
|
|
2143
|
+
["cancellation"],
|
|
2144
|
+
["compilation"],
|
|
2145
|
+
["deployment"]
|
|
2146
|
+
]
|
|
2147
|
+
},
|
|
2148
|
+
{ title: "Tooling", topics: [["cli-reference"], ["mcp-tools"], ["iterative-development"], ["marketplace"]] },
|
|
2149
|
+
{ title: "Reference", topics: [["error-codes", { codes: true }]] }
|
|
2150
|
+
];
|
|
2151
|
+
function guideOutline(topics = listTopics(), packSlugs = /* @__PURE__ */ new Set()) {
|
|
2152
|
+
const byslug = new Map(topics.map((t) => [t.slug, t]));
|
|
2153
|
+
const placed = /* @__PURE__ */ new Set();
|
|
2154
|
+
const groups = [];
|
|
2155
|
+
for (const g of GROUPS) {
|
|
2156
|
+
const entries = [];
|
|
2157
|
+
for (const [slug, related] of g.topics) {
|
|
2158
|
+
const t = byslug.get(slug);
|
|
2159
|
+
if (!t) continue;
|
|
2160
|
+
placed.add(slug);
|
|
2161
|
+
entries.push({ slug, name: t.name || slug, description: t.description, ...related ? { related } : {} });
|
|
2162
|
+
}
|
|
2163
|
+
if (entries.length) groups.push({ title: g.title, topics: entries });
|
|
2164
|
+
}
|
|
2165
|
+
const rest = topics.filter((t) => !placed.has(t.slug));
|
|
2166
|
+
const more = rest.filter((t) => !packSlugs.has(t.slug)).map((t) => ({ slug: t.slug, name: t.name || t.slug, description: t.description }));
|
|
2167
|
+
const packs = rest.filter((t) => packSlugs.has(t.slug)).map((t) => ({ slug: t.slug, name: t.name || t.slug, description: t.description }));
|
|
2168
|
+
if (more.length) groups.push({ title: "More", topics: more });
|
|
2169
|
+
if (packs.length) groups.push({ title: "Packs", topics: packs });
|
|
2170
|
+
return groups;
|
|
2171
|
+
}
|
|
2172
|
+
|
|
2173
|
+
// src/console/cli-catalog.ts
|
|
2174
|
+
var SKIP_GROUPS = /* @__PURE__ */ new Set(["Quick Reference", "Global Flag", "Related Topics"]);
|
|
2175
|
+
var cell = (s) => s.trim().replace(/^`|`$/g, "");
|
|
2176
|
+
function parseCliReference(markdown) {
|
|
2177
|
+
const out = [];
|
|
2178
|
+
let group = "";
|
|
2179
|
+
let cur;
|
|
2180
|
+
let fence = null;
|
|
2181
|
+
let examplesNext = false;
|
|
2182
|
+
let inFlags = false;
|
|
2183
|
+
let described = false;
|
|
2184
|
+
for (const raw of markdown.split("\n")) {
|
|
2185
|
+
const line = raw.trimEnd();
|
|
2186
|
+
const h2 = line.match(/^## (.+)$/);
|
|
2187
|
+
if (h2) {
|
|
2188
|
+
group = h2[1].trim();
|
|
2189
|
+
cur = void 0;
|
|
2190
|
+
fence = null;
|
|
2191
|
+
continue;
|
|
2192
|
+
}
|
|
2193
|
+
const h3 = line.match(/^### (.+)$/);
|
|
2194
|
+
if (h3) {
|
|
2195
|
+
if (SKIP_GROUPS.has(group)) {
|
|
2196
|
+
cur = void 0;
|
|
2197
|
+
continue;
|
|
2198
|
+
}
|
|
2199
|
+
cur = { name: h3[1].trim(), words: [], group, description: "", usage: "", flags: [], examples: [] };
|
|
2200
|
+
out.push(cur);
|
|
2201
|
+
fence = null;
|
|
2202
|
+
examplesNext = false;
|
|
2203
|
+
inFlags = false;
|
|
2204
|
+
described = false;
|
|
2205
|
+
continue;
|
|
2206
|
+
}
|
|
2207
|
+
if (!cur) continue;
|
|
2208
|
+
if (line.startsWith("```")) {
|
|
2209
|
+
if (fence) {
|
|
2210
|
+
fence = null;
|
|
2211
|
+
continue;
|
|
2212
|
+
}
|
|
2213
|
+
fence = examplesNext ? "examples" : !cur.usage ? "usage" : "other";
|
|
2214
|
+
examplesNext = false;
|
|
2215
|
+
continue;
|
|
2216
|
+
}
|
|
2217
|
+
if (fence === "usage") {
|
|
2218
|
+
if (!cur.usage && /^fw\b/.test(line)) {
|
|
2219
|
+
cur.usage = line;
|
|
2220
|
+
cur.words = line.split(/\s+/).slice(1).filter((w) => !/^[<[-]/.test(w));
|
|
2221
|
+
}
|
|
2222
|
+
continue;
|
|
2223
|
+
}
|
|
2224
|
+
if (fence === "examples") {
|
|
2225
|
+
if (line.trim() && !line.startsWith("#")) cur.examples.push(line.trim());
|
|
2226
|
+
continue;
|
|
2227
|
+
}
|
|
2228
|
+
if (fence) continue;
|
|
2229
|
+
if (/^\*\*Examples:?\*\*/.test(line)) {
|
|
2230
|
+
examplesNext = true;
|
|
2231
|
+
continue;
|
|
2232
|
+
}
|
|
2233
|
+
if (/^\|\s*Flag\s*\|/i.test(line)) {
|
|
2234
|
+
inFlags = true;
|
|
2235
|
+
continue;
|
|
2236
|
+
}
|
|
2237
|
+
if (inFlags) {
|
|
2238
|
+
if (!line.startsWith("|")) {
|
|
2239
|
+
inFlags = false;
|
|
2240
|
+
} else {
|
|
2241
|
+
const cells = line.split("|").slice(1, -1);
|
|
2242
|
+
if (cells.length >= 2 && !/^[\s-:]+$/.test(cells[0])) {
|
|
2243
|
+
cur.flags.push({ flag: cell(cells[0]), description: cells[1].trim(), default: cell(cells[2] ?? "") });
|
|
2244
|
+
}
|
|
2245
|
+
continue;
|
|
2246
|
+
}
|
|
2247
|
+
}
|
|
2248
|
+
if (!described && line.trim() && !line.startsWith("|") && !line.startsWith(">") && !line.startsWith("---")) {
|
|
2249
|
+
cur.description = line.trim();
|
|
2250
|
+
described = true;
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2253
|
+
return out.filter((c) => c.usage);
|
|
2254
|
+
}
|
|
2255
|
+
var cached;
|
|
2256
|
+
function cliCatalog() {
|
|
2257
|
+
if (!cached) cached = parseCliReference(readTopic("cli-reference")?.content ?? "");
|
|
2258
|
+
return cached;
|
|
2259
|
+
}
|
|
2260
|
+
|
|
2261
|
+
// src/console/export.ts
|
|
2262
|
+
import * as path9 from "node:path";
|
|
2263
|
+
var defaultOutputDir = (file, target) => path9.join(path9.dirname(file), "dist", target);
|
|
2264
|
+
async function listTargets(projectDir) {
|
|
2265
|
+
const registry = await createTargetRegistry(projectDir);
|
|
2266
|
+
const packs = await describePacks(projectDir);
|
|
2267
|
+
const owner = /* @__PURE__ */ new Map();
|
|
2268
|
+
for (const p of packs) for (const t of p.exportTargets) owner.set(t.name, p.name);
|
|
2269
|
+
return registry.getNames().map((name) => {
|
|
2270
|
+
const t = registry.get(name);
|
|
2271
|
+
return { name, description: t.description, pack: owner.get(name) ?? null, deploySchema: t.deploySchema ?? null };
|
|
2272
|
+
}).sort((a, b) => a.name.localeCompare(b.name));
|
|
2273
|
+
}
|
|
2274
|
+
async function runExport(projectDir, req) {
|
|
2275
|
+
const outputDir = path9.resolve(req.outputDir ?? defaultOutputDir(req.file, req.target));
|
|
2276
|
+
const result = await exportWorkflow({
|
|
2277
|
+
target: req.target,
|
|
2278
|
+
input: req.file,
|
|
2279
|
+
output: outputDir,
|
|
2280
|
+
workflow: req.name,
|
|
2281
|
+
dryRun: req.preview,
|
|
2282
|
+
production: true,
|
|
2283
|
+
projectDir
|
|
2284
|
+
});
|
|
2285
|
+
const files = result.files.map((f) => ({ path: path9.relative(outputDir, f.path).split(path9.sep).join("/"), content: f.content }));
|
|
2286
|
+
const registry = await createTargetRegistry(projectDir);
|
|
2287
|
+
const target = registry.get(req.target);
|
|
2288
|
+
let instructions = null;
|
|
2289
|
+
if (target && files.length) {
|
|
2290
|
+
const artifacts = {
|
|
2291
|
+
target: req.target,
|
|
2292
|
+
workflowName: result.workflow,
|
|
2293
|
+
entryPoint: files[0].path,
|
|
2294
|
+
files: files.map((f) => ({ relativePath: f.path, absolutePath: path9.join(outputDir, f.path), content: f.content, type: "other" })),
|
|
2295
|
+
warnings: result.warnings
|
|
2296
|
+
};
|
|
2297
|
+
try {
|
|
2298
|
+
instructions = target.getDeployInstructions(artifacts);
|
|
2299
|
+
} catch {
|
|
2300
|
+
instructions = null;
|
|
2301
|
+
}
|
|
2302
|
+
}
|
|
2303
|
+
return { target: req.target, outputDir, written: !req.preview, files, warnings: result.warnings ?? [], instructions };
|
|
2304
|
+
}
|
|
2305
|
+
|
|
2306
|
+
// src/console/author.ts
|
|
2307
|
+
import * as fs11 from "node:fs";
|
|
2308
|
+
import * as path10 from "node:path";
|
|
2309
|
+
var PACK_NAME = /^(@[^/]+\/)?flow-weaver-pack-.+$/;
|
|
2310
|
+
var PACK_KEYWORD = "flow-weaver-marketplace-pack";
|
|
2311
|
+
function detectPackProject(projectDir) {
|
|
2312
|
+
const file = path10.join(projectDir, "package.json");
|
|
2313
|
+
if (!fs11.existsSync(file)) return { isPack: false };
|
|
2314
|
+
try {
|
|
2315
|
+
const pkg = JSON.parse(fs11.readFileSync(file, "utf8"));
|
|
2316
|
+
const isPack = fs11.existsSync(path10.join(projectDir, "flowweaver.manifest.json")) || (pkg.keywords ?? []).includes(PACK_KEYWORD) || typeof pkg.name === "string" && PACK_NAME.test(pkg.name);
|
|
2317
|
+
return isPack ? { isPack, name: pkg.name, version: pkg.version } : { isPack: false };
|
|
2318
|
+
} catch {
|
|
2319
|
+
return { isPack: false };
|
|
2320
|
+
}
|
|
2321
|
+
}
|
|
2322
|
+
var names = (list) => new Set((list ?? []).map((x) => x.name));
|
|
2323
|
+
function manifestChanges(existing, next) {
|
|
2324
|
+
if (!existing) return ["No flowweaver.manifest.json yet. fw market pack writes it."];
|
|
2325
|
+
const out = [];
|
|
2326
|
+
if (existing.version !== next.version) out.push(`version ${existing.version} \u2192 ${next.version}`);
|
|
2327
|
+
if ((existing.description ?? "") !== (next.description ?? "")) out.push("description changed");
|
|
2328
|
+
for (const [label, was, now] of [
|
|
2329
|
+
["node type", names(existing.nodeTypes), names(next.nodeTypes)],
|
|
2330
|
+
["workflow", names(existing.workflows), names(next.workflows)]
|
|
2331
|
+
]) {
|
|
2332
|
+
for (const n of now) if (!was.has(n)) out.push(`${label} ${n} added`);
|
|
2333
|
+
for (const n of was) if (!now.has(n)) out.push(`${label} ${n} removed`);
|
|
2334
|
+
}
|
|
2335
|
+
for (const n of next.nodeTypes ?? []) {
|
|
2336
|
+
const e = (existing.nodeTypes ?? []).find((x) => x.name === n.name);
|
|
2337
|
+
if (e && JSON.stringify({ i: e.inputs, o: e.outputs, d: e.description }) !== JSON.stringify({ i: n.inputs, o: n.outputs, d: n.description })) out.push(`node type ${n.name} changed`);
|
|
2338
|
+
}
|
|
2339
|
+
return out;
|
|
2340
|
+
}
|
|
2341
|
+
async function checkPackProject(projectDir) {
|
|
2342
|
+
const { manifest, parsedFiles, errors } = await generateManifest({ directory: projectDir });
|
|
2343
|
+
const validation = await validatePackage(projectDir, manifest);
|
|
2344
|
+
const existing = readManifest(projectDir);
|
|
2345
|
+
return {
|
|
2346
|
+
name: manifest.name,
|
|
2347
|
+
version: manifest.version,
|
|
2348
|
+
parsedFiles: parsedFiles.length,
|
|
2349
|
+
parseErrors: errors,
|
|
2350
|
+
valid: validation.valid,
|
|
2351
|
+
issues: validation.issues,
|
|
2352
|
+
manifest: describePack({ name: manifest.name, version: manifest.version, manifest, path: projectDir }),
|
|
2353
|
+
hasManifest: existing !== null,
|
|
2354
|
+
changes: manifestChanges(existing, manifest)
|
|
2355
|
+
};
|
|
2356
|
+
}
|
|
2357
|
+
|
|
2358
|
+
// src/console/status.ts
|
|
2359
|
+
import * as fs12 from "node:fs";
|
|
2360
|
+
import * as os3 from "node:os";
|
|
2361
|
+
import * as path11 from "node:path";
|
|
2362
|
+
var realpath = (p) => {
|
|
2363
|
+
try {
|
|
2364
|
+
return fs12.realpathSync(p);
|
|
2365
|
+
} catch {
|
|
2366
|
+
return path11.resolve(p);
|
|
2367
|
+
}
|
|
2368
|
+
};
|
|
2369
|
+
function installVerdict(command, args, here = installRoot()) {
|
|
2370
|
+
const all = [command, ...args];
|
|
2371
|
+
if (all.some((a) => /@synergenius\/flow-weaver(@|$)/.test(a)) && /npx|pnpm|yarn|bunx/.test(command)) return { runs: "npm latest" };
|
|
2372
|
+
const pathArg = all.find((a) => /flow-weaver\.mjs$|[\\/]flow-weaver([\\/]|$)/.test(a) && /[\\/]/.test(a));
|
|
2373
|
+
if (!pathArg) return { runs: "unknown" };
|
|
2374
|
+
let install = realpath(pathArg);
|
|
2375
|
+
for (let i = 0; i < 6 && !fs12.existsSync(path11.join(install, "package.json")); i++) install = path11.dirname(install);
|
|
2376
|
+
return { runs: realpath(install) === realpath(here) ? "this install" : "other install", install };
|
|
2377
|
+
}
|
|
2378
|
+
function readJson(file) {
|
|
2379
|
+
try {
|
|
2380
|
+
return JSON.parse(fs12.readFileSync(file, "utf8"));
|
|
2381
|
+
} catch {
|
|
2382
|
+
return null;
|
|
2383
|
+
}
|
|
2384
|
+
}
|
|
2385
|
+
function mcpRegistrations(projectDir, home = os3.homedir(), here = installRoot()) {
|
|
2386
|
+
const out = [];
|
|
2387
|
+
const add = (tool, file, entry) => {
|
|
2388
|
+
if (!entry || typeof entry.command !== "string") return;
|
|
2389
|
+
const args = Array.isArray(entry.args) ? entry.args.map(String) : [];
|
|
2390
|
+
out.push({ tool, file, command: entry.command, args, ...installVerdict(entry.command, args, here) });
|
|
2391
|
+
};
|
|
2392
|
+
const servers = (file, key) => {
|
|
2393
|
+
const cfg = readJson(file);
|
|
2394
|
+
const map = cfg?.[key];
|
|
2395
|
+
return map?.["flow-weaver"];
|
|
2396
|
+
};
|
|
2397
|
+
add("Claude Code (project)", path11.join(projectDir, ".mcp.json"), servers(path11.join(projectDir, ".mcp.json"), "mcpServers"));
|
|
2398
|
+
const claude = readJson(path11.join(home, ".claude.json"));
|
|
2399
|
+
if (claude) {
|
|
2400
|
+
add("Claude Code (user)", path11.join(home, ".claude.json"), claude.mcpServers?.["flow-weaver"]);
|
|
2401
|
+
const projects = claude.projects;
|
|
2402
|
+
const mine = projects?.[projectDir] ?? projects?.[realpath(projectDir)];
|
|
2403
|
+
add("Claude Code (this project)", path11.join(home, ".claude.json"), mine?.mcpServers?.["flow-weaver"]);
|
|
2404
|
+
}
|
|
2405
|
+
add("Cursor", path11.join(projectDir, ".cursor", "mcp.json"), servers(path11.join(projectDir, ".cursor", "mcp.json"), "mcpServers"));
|
|
2406
|
+
add("VS Code", path11.join(projectDir, ".vscode", "mcp.json"), servers(path11.join(projectDir, ".vscode", "mcp.json"), "servers"));
|
|
2407
|
+
add("Windsurf", path11.join(home, ".codeium", "windsurf", "mcp_config.json"), servers(path11.join(home, ".codeium", "windsurf", "mcp_config.json"), "mcpServers"));
|
|
2408
|
+
add("OpenClaw", path11.join(projectDir, "openclaw.json"), servers(path11.join(projectDir, "openclaw.json"), "mcpServers"));
|
|
2409
|
+
return out;
|
|
2410
|
+
}
|
|
2411
|
+
async function probe(url, init, timeoutMs = 2500) {
|
|
2412
|
+
const started = Date.now();
|
|
2413
|
+
const ctl = new AbortController();
|
|
2414
|
+
const t = setTimeout(() => ctl.abort(), timeoutMs);
|
|
2415
|
+
try {
|
|
2416
|
+
const res = await fetch(url, { ...init, signal: ctl.signal });
|
|
2417
|
+
return { url, ok: res.ok, status: res.status, ms: Date.now() - started };
|
|
2418
|
+
} catch (err) {
|
|
2419
|
+
return { url, ok: false, ms: Date.now() - started, error: err instanceof Error ? err.name === "AbortError" ? "timed out" : err.message : String(err) };
|
|
2420
|
+
} finally {
|
|
2421
|
+
clearTimeout(t);
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2424
|
+
async function registryStatuses(projectDir, registries = resolveRegistries(projectDir)) {
|
|
2425
|
+
return Promise.all(registries.map(async (r) => {
|
|
2426
|
+
const url = `${r.url}-/whoami`;
|
|
2427
|
+
const started = Date.now();
|
|
2428
|
+
const ctl = new AbortController();
|
|
2429
|
+
const t = setTimeout(() => ctl.abort(), 2500);
|
|
2430
|
+
try {
|
|
2431
|
+
const res = await fetch(url, { headers: r.authorization ? { Authorization: r.authorization } : {}, signal: ctl.signal });
|
|
2432
|
+
let user;
|
|
2433
|
+
if (res.ok) {
|
|
2434
|
+
try {
|
|
2435
|
+
user = (await res.json()).username;
|
|
2436
|
+
} catch {
|
|
2437
|
+
}
|
|
2438
|
+
}
|
|
2439
|
+
const reachable = res.ok || !r.authorization && (res.status === 401 || res.status === 403);
|
|
2440
|
+
return { url: r.url, scopes: r.scopes, authenticated: !!r.authorization, ok: reachable, status: res.status, ms: Date.now() - started, user };
|
|
2441
|
+
} catch (err) {
|
|
2442
|
+
return { url: r.url, scopes: r.scopes, authenticated: !!r.authorization, ok: false, ms: Date.now() - started, error: err instanceof Error ? err.name === "AbortError" ? "timed out" : err.message : String(err) };
|
|
2443
|
+
} finally {
|
|
2444
|
+
clearTimeout(t);
|
|
2445
|
+
}
|
|
2446
|
+
}));
|
|
2447
|
+
}
|
|
2448
|
+
async function describeStatus(projectDir, info) {
|
|
2449
|
+
const services = listServices().map((s) => ({ ...s, alive: true }));
|
|
2450
|
+
const serve = services.filter((s) => s.kind === "serve" && s.url);
|
|
2451
|
+
const [http2, registries] = await Promise.all([
|
|
2452
|
+
Promise.all(serve.map(async (service) => ({ service, probe: await probe(`${service.url.replace(/\/$/, "")}/health`) }))),
|
|
2453
|
+
registryStatuses(projectDir)
|
|
2454
|
+
]);
|
|
2455
|
+
return {
|
|
2456
|
+
console: { version: VERSION, install: installRoot(), project: projectDir, url: info.url, watching: info.watching, runsDir: info.runsDir },
|
|
2457
|
+
services,
|
|
2458
|
+
mcp: { registrations: mcpRegistrations(projectDir), running: services.filter((s) => s.kind === "mcp-server") },
|
|
2459
|
+
doctor: runDoctorChecks(projectDir),
|
|
2460
|
+
http: http2,
|
|
2461
|
+
registries,
|
|
2462
|
+
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
2463
|
+
};
|
|
2464
|
+
}
|
|
2465
|
+
|
|
2466
|
+
// src/console/routes-tools.ts
|
|
2467
|
+
var CLI_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
2468
|
+
function toolRoutes(ctx) {
|
|
2469
|
+
return [
|
|
2470
|
+
// An empty or one-letter query matches every topic with an empty
|
|
2471
|
+
// heading and excerpt, which is noise rather than a result list.
|
|
2472
|
+
{
|
|
2473
|
+
path: "/api/docs/search",
|
|
2474
|
+
handle: ({ res, q }) => {
|
|
2475
|
+
const query = q("q").trim();
|
|
2476
|
+
const limit = Math.min(80, Math.max(1, Number(q("limit")) || 12));
|
|
2477
|
+
return json(res, 200, query.length < 2 ? [] : searchDocs(query).filter((r) => r.heading).slice(0, limit));
|
|
2478
|
+
}
|
|
2479
|
+
},
|
|
2480
|
+
{ path: "/api/docs/topics", handle: ({ res }) => json(res, 200, listTopics()) },
|
|
2481
|
+
{
|
|
2482
|
+
path: "/api/docs/guide",
|
|
2483
|
+
handle: ({ res }) => json(res, 200, guideOutline(listTopics(), new Set(getPackDocTopics().map((t) => t.slug))))
|
|
2484
|
+
},
|
|
2485
|
+
// The page as markdown for rendering, its sections for the contents
|
|
2486
|
+
// list, and the compact form -- what `fw_docs` hands an assistant --
|
|
2487
|
+
// for copying into a conversation.
|
|
2488
|
+
{
|
|
2489
|
+
path: "/api/docs/topic",
|
|
2490
|
+
handle: ({ res, q }) => {
|
|
2491
|
+
const slug = q("slug");
|
|
2492
|
+
const full = readTopic(slug);
|
|
2493
|
+
if (!full) return json(res, 404, { error: `no topic ${slug}` });
|
|
2494
|
+
const structured = readTopicStructured(slug);
|
|
2495
|
+
return json(res, 200, {
|
|
2496
|
+
slug,
|
|
2497
|
+
name: full.name,
|
|
2498
|
+
description: full.description,
|
|
2499
|
+
sections: structured?.sections.map((s) => ({ heading: s.heading, level: s.level })) ?? [],
|
|
2500
|
+
markdown: full.content,
|
|
2501
|
+
compact: readTopic(slug, true)?.content ?? ""
|
|
2502
|
+
});
|
|
2503
|
+
}
|
|
2504
|
+
},
|
|
2505
|
+
{ path: "/api/docs/error", handle: ({ res, q }) => json(res, 200, errorCodeSection(q("code"))) },
|
|
2506
|
+
// Core commands from the reference, then each installed pack's, so
|
|
2507
|
+
// completion knows `fw audio replay` as well as `fw validate`.
|
|
2508
|
+
{
|
|
2509
|
+
path: "/api/cli/commands",
|
|
2510
|
+
handle: async ({ res }) => {
|
|
2511
|
+
const packCommands = (await describePacks(ctx.projectDir())).flatMap((p) => p.cliCommands.map((c) => ({
|
|
2512
|
+
name: `${p.namespace} ${c.name}`,
|
|
2513
|
+
words: [p.namespace, c.name],
|
|
2514
|
+
group: `Pack: ${p.namespace}`,
|
|
2515
|
+
description: c.description,
|
|
2516
|
+
usage: c.usage,
|
|
2517
|
+
flags: c.flags,
|
|
2518
|
+
examples: []
|
|
2519
|
+
})));
|
|
2520
|
+
return json(res, 200, [...cliCatalog(), ...packCommands]);
|
|
2521
|
+
}
|
|
2522
|
+
},
|
|
2523
|
+
// An fw command, run in the project, its output streamed back. The
|
|
2524
|
+
// argument list is spawned as is -- no shell -- and the child goes
|
|
2525
|
+
// when the page lets go of the stream.
|
|
2526
|
+
{
|
|
2527
|
+
method: "POST",
|
|
2528
|
+
path: "/api/cli/run",
|
|
2529
|
+
handle: async ({ req, res, body }) => {
|
|
2530
|
+
const argv = (await body()).argv;
|
|
2531
|
+
const plan = planFwCommand(Array.isArray(argv) ? argv : []);
|
|
2532
|
+
if (!plan.ok) return json(res, 400, { error: plan.error });
|
|
2533
|
+
sse(res);
|
|
2534
|
+
const started = Date.now();
|
|
2535
|
+
const emit = (msg) => send(res, msg);
|
|
2536
|
+
emit({ type: "start", args: plan.args });
|
|
2537
|
+
let child;
|
|
2538
|
+
try {
|
|
2539
|
+
child = spawnFw(plan.args, ctx.projectDir());
|
|
2540
|
+
} catch (err) {
|
|
2541
|
+
emit({ type: "exit", code: null, error: getErrorMessage(err), ms: 0 });
|
|
2542
|
+
res.end();
|
|
2543
|
+
return;
|
|
2544
|
+
}
|
|
2545
|
+
child.stdout?.on("data", (d) => emit({ type: "out", text: d.toString() }));
|
|
2546
|
+
child.stderr?.on("data", (d) => emit({ type: "err", text: d.toString() }));
|
|
2547
|
+
const timer = setTimeout(() => child.kill(), CLI_TIMEOUT_MS);
|
|
2548
|
+
child.on("error", (err) => {
|
|
2549
|
+
emit({ type: "exit", code: null, error: err.message, ms: Date.now() - started });
|
|
2550
|
+
res.end();
|
|
2551
|
+
});
|
|
2552
|
+
child.on("close", (code) => {
|
|
2553
|
+
clearTimeout(timer);
|
|
2554
|
+
emit({ type: "exit", code, ms: Date.now() - started });
|
|
2555
|
+
res.end();
|
|
2556
|
+
});
|
|
2557
|
+
req.on("close", () => {
|
|
2558
|
+
clearTimeout(timer);
|
|
2559
|
+
if (child.exitCode === null) child.kill();
|
|
2560
|
+
});
|
|
2561
|
+
}
|
|
2562
|
+
},
|
|
2563
|
+
{ path: "/api/packs", handle: async ({ res }) => json(res, 200, await describePacks(ctx.projectDir())) },
|
|
2564
|
+
{ path: "/api/export/targets", handle: async ({ res }) => json(res, 200, await listTargets(ctx.projectDir())) },
|
|
2565
|
+
{
|
|
2566
|
+
method: "POST",
|
|
2567
|
+
path: "/api/export",
|
|
2568
|
+
handle: async ({ res, body }) => {
|
|
2569
|
+
const b = await body();
|
|
2570
|
+
const target = ctx.inProject(String(b.file));
|
|
2571
|
+
if (!target) return json(res, 400, { error: "file is outside the project" });
|
|
2572
|
+
try {
|
|
2573
|
+
return json(res, 200, await runExport(ctx.projectDir(), {
|
|
2574
|
+
file: target,
|
|
2575
|
+
name: String(b.name),
|
|
2576
|
+
target: String(b.target),
|
|
2577
|
+
outputDir: typeof b.outputDir === "string" && b.outputDir ? path12.resolve(ctx.projectDir(), b.outputDir) : void 0,
|
|
2578
|
+
preview: b.preview !== false
|
|
2579
|
+
}));
|
|
2580
|
+
} catch (err) {
|
|
2581
|
+
return json(res, 400, { error: getErrorMessage(err) });
|
|
2582
|
+
}
|
|
2583
|
+
}
|
|
2584
|
+
},
|
|
2585
|
+
// The project as a pack: cheap to detect, slow to check (every source
|
|
2586
|
+
// file is parsed), so the check is a separate call made on request.
|
|
2587
|
+
{ path: "/api/pack-project", handle: ({ res }) => json(res, 200, detectPackProject(ctx.projectDir())) },
|
|
2588
|
+
{
|
|
2589
|
+
path: "/api/pack-project/check",
|
|
2590
|
+
handle: async ({ res }) => {
|
|
2591
|
+
if (!detectPackProject(ctx.projectDir()).isPack) return json(res, 400, { error: "the project is not a pack" });
|
|
2592
|
+
try {
|
|
2593
|
+
return json(res, 200, await checkPackProject(ctx.projectDir()));
|
|
2594
|
+
} catch (err) {
|
|
2595
|
+
return json(res, 400, { error: getErrorMessage(err) });
|
|
2596
|
+
}
|
|
2597
|
+
}
|
|
2598
|
+
},
|
|
2599
|
+
// The marketplace is npm: a search goes to the registry, and installing
|
|
2600
|
+
// is `fw market install`, which the command-line pane runs when asked
|
|
2601
|
+
// to. Every registry the project's npm uses is searched, credentials
|
|
2602
|
+
// included: a private pack is on a private registry, and .npmrc says which.
|
|
2603
|
+
{
|
|
2604
|
+
path: "/api/market/search",
|
|
2605
|
+
handle: async ({ res, q }) => json(res, 200, await searchAllRegistries({ query: q("q") || void 0, limit: 30, projectDir: ctx.projectDir() }))
|
|
2606
|
+
},
|
|
2607
|
+
// Everything around the project: services alive, MCP registrations,
|
|
2608
|
+
// the environment and registries. Probes are brief.
|
|
2609
|
+
{ path: "/api/status", handle: async ({ res }) => json(res, 200, await describeStatus(ctx.projectDir(), ctx.status())) }
|
|
2610
|
+
];
|
|
2611
|
+
}
|
|
2612
|
+
|
|
2613
|
+
// src/console/routes-runs.ts
|
|
2614
|
+
async function debugAction(ctx, id, { res, body }) {
|
|
2615
|
+
const { debug } = ctx.runs;
|
|
2616
|
+
const d = debug.get(id);
|
|
2617
|
+
if (!d) return json(res, 404, { error: "not a debug session" });
|
|
2618
|
+
const b = await body();
|
|
2619
|
+
const action = String(b.action);
|
|
2620
|
+
try {
|
|
2621
|
+
if (action === "step" || action === "continue" || action === "toBreakpoint") {
|
|
2622
|
+
if (d.status !== "paused") return json(res, 400, { error: `the session is ${d.status}, not paused` });
|
|
2623
|
+
const move = action === "step" ? debug.step(id) : debug.continue(id, action === "toBreakpoint");
|
|
2624
|
+
move.catch(() => void 0);
|
|
2625
|
+
} else if (action === "abort") {
|
|
2626
|
+
debug.abort(id).catch(() => void 0);
|
|
2627
|
+
} else if (action === "set") {
|
|
2628
|
+
debug.setVariable(id, String(b.node), String(b.port), b.value);
|
|
2629
|
+
ctx.runs.announce(id, { type: "event", t: Date.now(), e: { type: "VARIABLE_SET", identifier: { id: String(b.node), portName: String(b.port) }, value: b.value } });
|
|
2630
|
+
} else if (action === "breakpoint") {
|
|
2631
|
+
debug.breakpoint(id, b.op === "remove" ? "remove" : "add", String(b.node));
|
|
2632
|
+
} else {
|
|
2633
|
+
return json(res, 400, { error: `unknown action ${action}` });
|
|
2634
|
+
}
|
|
2635
|
+
return json(res, 200, await ctx.runs.snapshot(id));
|
|
2636
|
+
} catch (err) {
|
|
2637
|
+
return json(res, 400, { error: getErrorMessage(err) });
|
|
2638
|
+
}
|
|
2639
|
+
}
|
|
2640
|
+
function runRoutes(ctx) {
|
|
2641
|
+
const { runs } = ctx;
|
|
2642
|
+
return [
|
|
2643
|
+
{ method: "GET", path: "/api/runs", handle: async ({ res, q }) => json(res, 200, await runs.list(q("file"), q("name"))) },
|
|
2644
|
+
{
|
|
2645
|
+
method: "POST",
|
|
2646
|
+
path: "/api/runs",
|
|
2647
|
+
handle: async ({ res, body }) => {
|
|
2648
|
+
const b = await body();
|
|
2649
|
+
const target = ctx.inProject(String(b.file));
|
|
2650
|
+
if (!target) return json(res, 400, { error: "file is outside the project" });
|
|
2651
|
+
const mocks = mocksFrom(b);
|
|
2652
|
+
const source = await stamp(target).catch(() => void 0);
|
|
2653
|
+
const params = b.params ?? {};
|
|
2654
|
+
if (b.debug === true) {
|
|
2655
|
+
const bps = Array.isArray(b.breakpoints) ? b.breakpoints.filter((x) => typeof x === "string") : [];
|
|
2656
|
+
return json(res, 200, runs.startDebug(target, String(b.name), params, bps, mocks, b.runTo === "breakpoint" ? "breakpoint" : "first", source));
|
|
2657
|
+
}
|
|
2658
|
+
return json(res, 200, await runs.start(target, String(b.name), params, mocks, source, b.agents === "manual" ? "manual" : "auto"));
|
|
2659
|
+
}
|
|
2660
|
+
},
|
|
2661
|
+
{
|
|
2662
|
+
path: /^\/api\/runs\/([^/]+)(?:\/(events|resolve|cancel|debug|agent))?$/,
|
|
2663
|
+
handle: async (call) => {
|
|
2664
|
+
const { req, res, q, body, match } = call;
|
|
2665
|
+
const id = match[1];
|
|
2666
|
+
const sub = match[2];
|
|
2667
|
+
if (!await runs.snapshot(id)) return json(res, 404, { error: "no such run" });
|
|
2668
|
+
if (!sub && req.method === "DELETE") {
|
|
2669
|
+
try {
|
|
2670
|
+
await runs.remove(id);
|
|
2671
|
+
} catch (err) {
|
|
2672
|
+
return json(res, 409, { error: getErrorMessage(err) });
|
|
2673
|
+
}
|
|
2674
|
+
return json(res, 200, { removed: id });
|
|
2675
|
+
}
|
|
2676
|
+
if (sub === "events") return runs.watch(id, req, res);
|
|
2677
|
+
if (sub === "resolve" && req.method === "POST") {
|
|
2678
|
+
try {
|
|
2679
|
+
await runs.resume(id, await body());
|
|
2680
|
+
return json(res, 200, await runs.snapshot(id));
|
|
2681
|
+
} catch (err) {
|
|
2682
|
+
return json(res, 400, { error: getErrorMessage(err) });
|
|
2683
|
+
}
|
|
2684
|
+
}
|
|
2685
|
+
if (sub === "agent" && req.method === "POST") {
|
|
2686
|
+
const refused = await runs.askAgent(id);
|
|
2687
|
+
if (refused) return json(res, 409, { error: refused });
|
|
2688
|
+
return json(res, 200, await runs.snapshot(id));
|
|
2689
|
+
}
|
|
2690
|
+
if (sub === "agent" && req.method === "GET") {
|
|
2691
|
+
const rec = await ctx.runs.record(id);
|
|
2692
|
+
const gateId = q("gate") || rec?.agent?.gateId;
|
|
2693
|
+
const kept = gateId ? await ctx.runs.kept(id, transcriptName(gateId)) : void 0;
|
|
2694
|
+
if (!kept) return json(res, 404, { error: "no agent has answered this run" });
|
|
2695
|
+
return json(res, 200, kept);
|
|
2696
|
+
}
|
|
2697
|
+
if (sub === "cancel" && req.method === "POST") {
|
|
2698
|
+
await runs.cancel(id);
|
|
2699
|
+
return json(res, 200, await runs.snapshot(id));
|
|
2700
|
+
}
|
|
2701
|
+
if (sub === "debug" && req.method === "POST") return debugAction(ctx, id, call);
|
|
2702
|
+
return json(res, 200, await runs.withEvents(id));
|
|
2703
|
+
}
|
|
2704
|
+
}
|
|
2705
|
+
];
|
|
2706
|
+
}
|
|
2707
|
+
|
|
2708
|
+
// src/console/router.ts
|
|
2709
|
+
function findRoute(routes, method, pathname) {
|
|
2710
|
+
for (const route of routes) {
|
|
2711
|
+
if (route.method && route.method !== method) continue;
|
|
2712
|
+
if (typeof route.path === "string") {
|
|
2713
|
+
if (route.path === pathname) return { route, match: [] };
|
|
2714
|
+
} else {
|
|
2715
|
+
const match = pathname.match(route.path);
|
|
2716
|
+
if (match) return { route, match };
|
|
2717
|
+
}
|
|
2718
|
+
}
|
|
2719
|
+
return void 0;
|
|
2720
|
+
}
|
|
2721
|
+
|
|
2722
|
+
// src/console/request-guard.ts
|
|
2723
|
+
var SAFE_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD", "OPTIONS"]);
|
|
2724
|
+
function hostName(host) {
|
|
2725
|
+
const bracketed = host.match(/^\[[^\]]*\]/);
|
|
2726
|
+
return (bracketed ? bracketed[0] : host.replace(/:\d*$/, "")).toLowerCase();
|
|
2727
|
+
}
|
|
2728
|
+
function refusal(req, bound) {
|
|
2729
|
+
const host = typeof req.headers.host === "string" ? req.headers.host : "";
|
|
2730
|
+
if (!host) return "the request has no Host header";
|
|
2731
|
+
if (isLoopback(bound) && !isLoopback(hostName(host))) {
|
|
2732
|
+
return `the console answers only to a loopback Host, not ${host}`;
|
|
2733
|
+
}
|
|
2734
|
+
const origin = req.headers.origin;
|
|
2735
|
+
if (origin === void 0 || SAFE_METHODS.has((req.method ?? "GET").toUpperCase())) return void 0;
|
|
2736
|
+
if (origin !== `http://${host}`) {
|
|
2737
|
+
return `a change is accepted only from the console's own origin, not ${String(origin)}`;
|
|
2738
|
+
}
|
|
2739
|
+
return void 0;
|
|
2740
|
+
}
|
|
2741
|
+
|
|
2742
|
+
// src/console/server.ts
|
|
2743
|
+
function resolveAssets(explicit) {
|
|
2744
|
+
const here = path13.dirname(fileURLToPath2(import.meta.url));
|
|
2745
|
+
const candidates = [explicit, here, path13.join(here, "..", "console"), path13.join(here, "..", "..", "dist", "console")].filter(Boolean);
|
|
2746
|
+
return candidates.find((dir) => fs13.existsSync(path13.join(dir, "index.html")) && fs13.existsSync(path13.join(dir, "app.js")));
|
|
2747
|
+
}
|
|
2748
|
+
async function devBuild(onRebuilt) {
|
|
2749
|
+
const here = path13.dirname(fileURLToPath2(import.meta.url));
|
|
2750
|
+
const uiDir = path13.join(here, "..", "..", "console-ui");
|
|
2751
|
+
if (!fs13.existsSync(path13.join(uiDir, "src", "main.tsx"))) return void 0;
|
|
2752
|
+
let esbuild;
|
|
2753
|
+
try {
|
|
2754
|
+
esbuild = await import("esbuild");
|
|
2755
|
+
} catch {
|
|
2756
|
+
return void 0;
|
|
2757
|
+
}
|
|
2758
|
+
const out = path13.join(here, "..", "..", "dist", ".console-dev");
|
|
2759
|
+
fs13.mkdirSync(out, { recursive: true });
|
|
2760
|
+
const ctx = await esbuild.context({
|
|
2761
|
+
entryPoints: [path13.join(uiDir, "src", "main.tsx")],
|
|
2762
|
+
bundle: true,
|
|
2763
|
+
outfile: path13.join(out, "app.js"),
|
|
2764
|
+
format: "esm",
|
|
2765
|
+
target: "es2022",
|
|
2766
|
+
jsx: "automatic",
|
|
2767
|
+
jsxImportSource: "preact",
|
|
2768
|
+
sourcemap: "inline",
|
|
2769
|
+
logLevel: "warning",
|
|
2770
|
+
absWorkingDir: uiDir,
|
|
2771
|
+
plugins: [{ name: "reload", setup(b) {
|
|
2772
|
+
b.onEnd((res) => {
|
|
2773
|
+
if (!res.errors.length) onRebuilt();
|
|
2774
|
+
});
|
|
2775
|
+
} }]
|
|
2776
|
+
});
|
|
2777
|
+
await ctx.watch();
|
|
2778
|
+
const chokidar = await import("chokidar");
|
|
2779
|
+
chokidar.watch([path13.join(uiDir, "index.html"), path13.join(uiDir, "styles.css")], { ignoreInitial: true }).on("all", onRebuilt);
|
|
2780
|
+
return { serveFrom: uiDir, scriptFrom: out };
|
|
2781
|
+
}
|
|
2782
|
+
function within(root, p) {
|
|
2783
|
+
if (!p) return null;
|
|
2784
|
+
const abs = path13.resolve(p);
|
|
2785
|
+
const fold = (x) => path13.sep === "\\" || process.platform === "darwin" ? x.toLowerCase() : x;
|
|
2786
|
+
const a = fold(abs);
|
|
2787
|
+
const r = fold(root);
|
|
2788
|
+
return a === r || a.startsWith(r + path13.sep) ? abs : null;
|
|
2789
|
+
}
|
|
2790
|
+
async function createConsoleServer(options) {
|
|
2791
|
+
let projectDir = path13.resolve(options.projectDir);
|
|
2792
|
+
const host = options.host ?? "127.0.0.1";
|
|
2793
|
+
const port = options.port ?? 4311;
|
|
2794
|
+
const watching = options.watch !== false;
|
|
2795
|
+
const followers = /* @__PURE__ */ new Set();
|
|
2796
|
+
const broadcast = (msg) => {
|
|
2797
|
+
for (const res of followers) send(res, msg);
|
|
2798
|
+
};
|
|
2799
|
+
const runsDir = defaultRunsDir(projectDir);
|
|
2800
|
+
const coordinator = createLocalCoordinator(options.store ? { store: options.store } : { rootDir: runsDir });
|
|
2801
|
+
await reclaimStaleAgentAnswers(coordinator).catch(() => void 0);
|
|
2802
|
+
const profiles = createProfileCache(() => projectDir);
|
|
2803
|
+
const runs = createRunDriver({ coordinator, broadcast, projectDir: () => projectDir, profiles: () => profiles.get() });
|
|
2804
|
+
const superviseFor = (dir) => new Supervisor({
|
|
2805
|
+
projectDir: dir,
|
|
2806
|
+
...options.services,
|
|
2807
|
+
onChange: (e) => {
|
|
2808
|
+
if (!e.line) broadcast({ type: "services", kind: e.kind, state: e.state, url: e.url ?? null, exitCode: e.exitCode ?? null, error: e.error ?? null });
|
|
2809
|
+
}
|
|
2810
|
+
});
|
|
2811
|
+
let supervisor = superviseFor(projectDir);
|
|
2812
|
+
let checking;
|
|
2813
|
+
function queueCheck() {
|
|
2814
|
+
if (checking) return;
|
|
2815
|
+
const dir = projectDir;
|
|
2816
|
+
checking = checkWorkflows(dir, (summary) => {
|
|
2817
|
+
if (dir === projectDir) broadcast({ type: "checked", workflow: runs.withWaiting(summary) });
|
|
2818
|
+
}).then(() => void 0).catch(() => void 0).finally(() => {
|
|
2819
|
+
checking = void 0;
|
|
2820
|
+
});
|
|
2821
|
+
}
|
|
2822
|
+
let watcher;
|
|
2823
|
+
async function watchProject() {
|
|
2824
|
+
if (!watching) return;
|
|
2825
|
+
await watcher?.close();
|
|
2826
|
+
const chokidar = await import("chokidar");
|
|
2827
|
+
watcher = chokidar.watch(projectDir, {
|
|
2828
|
+
ignoreInitial: true,
|
|
2829
|
+
ignored: (p) => /(^|[\\/])(node_modules|dist|\.git|\.fw)([\\/]|$)/.test(p) || /(^|[\\/])\.fw-diff-/.test(p)
|
|
2830
|
+
});
|
|
2831
|
+
watcher.on("all", (_event, file) => {
|
|
2832
|
+
if (typeof file === "string" && file.endsWith(".ts")) {
|
|
2833
|
+
invalidateListing(projectDir);
|
|
2834
|
+
broadcast({ type: "changed", file });
|
|
2835
|
+
}
|
|
2836
|
+
});
|
|
2837
|
+
}
|
|
2838
|
+
await watchProject();
|
|
2839
|
+
await loadPackDocTopics(projectDir).catch(() => 0);
|
|
2840
|
+
await runs.follow({ watching, runsDir: options.store ? void 0 : runsDir });
|
|
2841
|
+
const heartbeat = setInterval(() => {
|
|
2842
|
+
for (const res of followers) res.write(": ping\n\n");
|
|
2843
|
+
runs.ping();
|
|
2844
|
+
}, 15e3);
|
|
2845
|
+
const dev = options.assetsDir ? void 0 : await devBuild(() => broadcast({ type: "client" }));
|
|
2846
|
+
const built = dev ? void 0 : resolveAssets(options.assetsDir);
|
|
2847
|
+
if (!built && !dev) throw new Error("console client not found: run `npm run build:console`, or pass assetsDir");
|
|
2848
|
+
let actualPort = port;
|
|
2849
|
+
const ctx = {
|
|
2850
|
+
projectDir: () => projectDir,
|
|
2851
|
+
async openProject(dir) {
|
|
2852
|
+
projectDir = dir;
|
|
2853
|
+
invalidateListing(projectDir);
|
|
2854
|
+
await supervisor.close().catch(() => void 0);
|
|
2855
|
+
supervisor = superviseFor(projectDir);
|
|
2856
|
+
await watchProject();
|
|
2857
|
+
await loadPackDocTopics(projectDir).catch(() => 0);
|
|
2858
|
+
options.onProject?.(projectDir);
|
|
2859
|
+
broadcast({ type: "project" });
|
|
2860
|
+
},
|
|
2861
|
+
inProject: (p) => within(projectDir, p),
|
|
2862
|
+
broadcast,
|
|
2863
|
+
followers,
|
|
2864
|
+
queueCheck,
|
|
2865
|
+
supervisor: () => supervisor,
|
|
2866
|
+
profiles,
|
|
2867
|
+
runs,
|
|
2868
|
+
status: () => ({ url: `http://${host}:${actualPort}`, watching, runsDir: options.store ? "a run store of your own" : runsDir }),
|
|
2869
|
+
client: { pageDir: dev?.serveFrom ?? built, scriptDir: dev?.scriptFrom ?? built }
|
|
2870
|
+
};
|
|
2871
|
+
const routes = [...projectRoutes(ctx), ...toolRoutes(ctx), ...agentRoutes(ctx), ...serviceRoutes(ctx), ...runRoutes(ctx)];
|
|
2872
|
+
const handle = async (req, res) => {
|
|
2873
|
+
const refused = refusal(req, host);
|
|
2874
|
+
if (refused) return json(res, 403, { error: refused });
|
|
2875
|
+
let url;
|
|
2876
|
+
try {
|
|
2877
|
+
url = new URL(req.url ?? "/", `http://${host}`);
|
|
2878
|
+
} catch {
|
|
2879
|
+
return json(res, 400, { error: "the request target is not a valid URL" });
|
|
2880
|
+
}
|
|
2881
|
+
try {
|
|
2882
|
+
const found = findRoute(routes, req.method ?? "GET", url.pathname);
|
|
2883
|
+
if (!found) return json(res, 404, { error: "not found" });
|
|
2884
|
+
await found.route.handle({ req, res, url, match: found.match, q: (k) => url.searchParams.get(k) ?? "", body: () => readBody(req) });
|
|
2885
|
+
} catch (err) {
|
|
2886
|
+
if (res.headersSent) {
|
|
2887
|
+
res.destroy();
|
|
2888
|
+
return;
|
|
2889
|
+
}
|
|
2890
|
+
json(res, err instanceof BadRequest ? 400 : 500, { error: getErrorMessage(err) });
|
|
2891
|
+
}
|
|
2892
|
+
};
|
|
2893
|
+
const server = http.createServer((req, res) => {
|
|
2894
|
+
handle(req, res).catch(() => res.destroy());
|
|
2895
|
+
});
|
|
2896
|
+
await new Promise((resolve9, reject) => {
|
|
2897
|
+
server.once("error", reject);
|
|
2898
|
+
server.listen(port, host, () => resolve9());
|
|
2899
|
+
});
|
|
2900
|
+
const address = server.address();
|
|
2901
|
+
actualPort = typeof address === "object" && address ? address.port : port;
|
|
2902
|
+
for (const kind of ["serve", "watch"]) {
|
|
2903
|
+
if (supervisor.settings()[kind].autoStart) {
|
|
2904
|
+
try {
|
|
2905
|
+
supervisor.start(kind);
|
|
2906
|
+
} catch {
|
|
2907
|
+
}
|
|
2908
|
+
}
|
|
2909
|
+
}
|
|
2910
|
+
return {
|
|
2911
|
+
url: `http://${host}:${actualPort}`,
|
|
2912
|
+
async close() {
|
|
2913
|
+
clearInterval(heartbeat);
|
|
2914
|
+
await supervisor.close().catch(() => void 0);
|
|
2915
|
+
await watcher?.close();
|
|
2916
|
+
await runs.close();
|
|
2917
|
+
for (const res of followers) res.end();
|
|
2918
|
+
await new Promise((resolve9) => server.close(() => resolve9()));
|
|
2919
|
+
}
|
|
2920
|
+
};
|
|
2921
|
+
}
|
|
2922
|
+
export {
|
|
2923
|
+
createConsoleServer,
|
|
2924
|
+
stepLabel
|
|
2925
|
+
};
|