sdn-flow 0.2.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.
Files changed (69) hide show
  1. package/.claude/SKILLS.md +7 -0
  2. package/.claude/skills/sdn-plugin-abi-compliance/SKILL.md +56 -0
  3. package/.claude/todo/001-js-host-startup-and-deno.md +85 -0
  4. package/LICENSE +21 -0
  5. package/README.md +223 -0
  6. package/bin/sdn-flow-host.js +169 -0
  7. package/docs/.nojekyll +0 -0
  8. package/docs/ARCHITECTURE.md +200 -0
  9. package/docs/HOST_CAPABILITY_MODEL.md +317 -0
  10. package/docs/PLUGIN_ARCHITECTURE.md +145 -0
  11. package/docs/PLUGIN_COMPATIBILITY.md +61 -0
  12. package/docs/PLUGIN_COMPLIANCE_CHECKS.md +82 -0
  13. package/docs/PLUGIN_MANIFEST.md +94 -0
  14. package/docs/css/style.css +465 -0
  15. package/docs/index.html +218 -0
  16. package/docs/js/app.mjs +751 -0
  17. package/docs/js/editor-panel.mjs +203 -0
  18. package/docs/js/flow-canvas.mjs +515 -0
  19. package/docs/js/flow-model.mjs +391 -0
  20. package/docs/js/workers/emception.worker.js +146 -0
  21. package/docs/js/workers/pyodide.worker.js +134 -0
  22. package/native/flow_source_generator.cpp +1958 -0
  23. package/package.json +67 -0
  24. package/schemas/FlowRuntimeAbi.fbs +91 -0
  25. package/src/auth/canonicalize.js +5 -0
  26. package/src/auth/index.js +11 -0
  27. package/src/auth/permissions.js +8 -0
  28. package/src/compiler/CppFlowSourceGenerator.js +475 -0
  29. package/src/compiler/EmceptionCompilerAdapter.js +244 -0
  30. package/src/compiler/SignedArtifactCatalog.js +152 -0
  31. package/src/compiler/index.js +8 -0
  32. package/src/compiler/nativeFlowSourceGeneratorTool.js +144 -0
  33. package/src/compliance/index.js +13 -0
  34. package/src/compliance/pluginCompliance.js +11 -0
  35. package/src/deploy/FlowDeploymentClient.js +532 -0
  36. package/src/deploy/index.js +8 -0
  37. package/src/designer/FlowDesignerSession.js +158 -0
  38. package/src/designer/index.js +2 -0
  39. package/src/designer/requirements.js +184 -0
  40. package/src/generated/runtimeAbiLayouts.js +544 -0
  41. package/src/host/appHost.js +105 -0
  42. package/src/host/autoHost.js +113 -0
  43. package/src/host/browserHostAdapters.js +108 -0
  44. package/src/host/compiledFlowRuntimeHost.js +703 -0
  45. package/src/host/constants.js +55 -0
  46. package/src/host/dependencyRuntime.js +227 -0
  47. package/src/host/descriptorAbi.js +351 -0
  48. package/src/host/fetchService.js +237 -0
  49. package/src/host/httpHostAdapters.js +280 -0
  50. package/src/host/index.js +91 -0
  51. package/src/host/installedFlowHost.js +885 -0
  52. package/src/host/invocationAbi.js +440 -0
  53. package/src/host/normalize.js +372 -0
  54. package/src/host/packageManagers.js +369 -0
  55. package/src/host/profile.js +134 -0
  56. package/src/host/runtimeAbi.js +106 -0
  57. package/src/host/workspace.js +895 -0
  58. package/src/index.js +8 -0
  59. package/src/runtime/FlowRuntime.js +273 -0
  60. package/src/runtime/MethodRegistry.js +295 -0
  61. package/src/runtime/constants.js +44 -0
  62. package/src/runtime/index.js +19 -0
  63. package/src/runtime/normalize.js +377 -0
  64. package/src/transport/index.js +7 -0
  65. package/src/transport/pki.js +7 -0
  66. package/src/utils/crypto.js +7 -0
  67. package/src/utils/encoding.js +65 -0
  68. package/src/utils/wasmCrypto.js +69 -0
  69. package/tools/run-plugin-compliance-check.mjs +153 -0
@@ -0,0 +1,7 @@
1
+ # Shared SDN Skills
2
+
3
+ ## Available skills
4
+
5
+ 1. SDN Plugin ABI & Compliance
6
+ - Path: `.claude/skills/sdn-plugin-abi-compliance/SKILL.md`
7
+ - Use when touching plugin manifests, plugin API/ABI exports, compiled artifact compliance, or cross-repo SDN plugin architecture.
@@ -0,0 +1,56 @@
1
+ # SDN Plugin ABI & Compliance
2
+
3
+ Use this skill whenever a task touches:
4
+
5
+ - plugin API/ABI rules
6
+ - manifest shape
7
+ - embedded manifest exports
8
+ - plugin compliance gates
9
+ - cross-repo SDN plugin architecture
10
+
11
+ ## Canonical source of truth
12
+
13
+ - `docs/PLUGIN_ARCHITECTURE.md`
14
+ - `docs/PLUGIN_MANIFEST.md`
15
+ - `docs/HOST_CAPABILITY_MODEL.md`
16
+ - `docs/PLUGIN_COMPATIBILITY.md`
17
+ - `docs/PLUGIN_COMPLIANCE_CHECKS.md`
18
+
19
+ ## Hard rules
20
+
21
+ 1. A plugin is a degenerate one-node flow.
22
+ 2. Deployable artifacts embed a FlatBuffer manifest.
23
+ 3. Plugin artifacts expose:
24
+ - `plugin_get_manifest_flatbuffer`
25
+ - `plugin_get_manifest_flatbuffer_size`
26
+ 4. Hosts bridge capabilities; they do not fork the plugin ABI.
27
+ 5. Capability and external-interface requirements must be explicit.
28
+
29
+ ## Compliance workflow
30
+
31
+ 1. Validate the manifest contract first.
32
+ 2. Validate compiled ABI exports when a `.wasm` artifact exists.
33
+ 3. Do not invent repo-local ABI variants.
34
+ 4. Use the shared checker in this repo instead of copying new compliance logic elsewhere.
35
+
36
+ ## Commands
37
+
38
+ Repo scan:
39
+
40
+ ```bash
41
+ node tools/run-plugin-compliance-check.mjs --repo-root .
42
+ ```
43
+
44
+ If a repo needs targeted scanning, add `sdn-plugin-compliance.json` at its root
45
+ and keep only manifest paths or scan directories there.
46
+
47
+ Manifest plus wasm artifact:
48
+
49
+ ```bash
50
+ node tools/run-plugin-compliance-check.mjs --manifest ./manifest.json --wasm ./dist/plugin.wasm
51
+ ```
52
+
53
+ ## Cross-repo rule
54
+
55
+ Other Space Data Network repos should call this checker through thin wrapper
56
+ scripts only. The actual API/ABI logic must live here in `sdn-flow`.
@@ -0,0 +1,85 @@
1
+ 1. Add explicit JS-host engine modeling for `node`, `deno`, `bun`, and `browser`. (done)
2
+
3
+ 2. Keep one `sdn-js` adapter family while allowing environment-specific compatibility summaries. (done)
4
+
5
+ 3. Add installed plugin package discovery for Node/JS hosts so packages can be scanned from directories with `manifest.json` and optional `plugin.js`. (done)
6
+
7
+ 4. Add environment-neutral installed plugin loading so browser/Deno hosts can supply in-memory manifests/modules instead of filesystem discovery. (done)
8
+
9
+ 5. Add a bootstrap API that discovers/registers installed plugins and loads a flow program into a runtime. (done)
10
+
11
+ 6. Make the bootstrap API usable for Node-RED-style “install nodes and startup” flows. (done)
12
+ Done:
13
+ - `createInstalledFlowHost(...)`
14
+ - package discovery and registration
15
+ - flow program load + runtime drain bootstrap
16
+ - `createInstalledFlowService(...)`
17
+ - portable timer trigger scheduling
18
+ - portable HTTP request trigger dispatch
19
+ - `host.refreshPlugins(...)`
20
+ - `service.refresh(...)`
21
+ - runnable top-level bootstrap examples and scripts for each concrete host adapter
22
+ - persisted package catalog plus package-manager-driven install/update/remove flows
23
+
24
+ 7. Add Deno-oriented host-plan support so `sdn-js` deployments can declare `engine: "deno"` and document single-file deployment intent. (done)
25
+
26
+ 8. Add compatibility reporting so the same flow can be evaluated against `browser`, `deno`, `node`, and other supported engines with explicit unsupported capabilities. (done)
27
+
28
+ 9. Add tests for plugin discovery, installed-package registration, startup bootstrap, and browser/deno compatibility summaries. (done)
29
+
30
+ 10. Update README and environment examples once the bootstrap and engine model land. (done)
31
+
32
+ 11. Add a fetch-style host adapter so Deno/browser/Bun/modern Node can bind installed flows to a shared `Request`/`Response` entrypoint. (done)
33
+
34
+ 12. Add a persisted workspace/bootstrap layer so installed-flow hosts can boot from one workspace file with flow paths, plugin roots, and host defaults. (done)
35
+
36
+ 13. Add explicit workspace package-catalog mutation so hosts can persist install/uninstall operations and refresh the runtime against that state. (done)
37
+
38
+ 14. Add a host-plan launcher that can auto-bind installed-flow HTTP listeners through an injected serve adapter. (done)
39
+
40
+ 15. Add concrete Deno- and Node-oriented HTTP host adapters on top of the installed-flow launcher surface. (done)
41
+
42
+ 16. Add concrete browser/worker fetch-host adapters on top of the installed-flow launcher surface. (done)
43
+
44
+ 17. Add concrete Bun HTTP host adapters on top of the installed-flow launcher surface. (done)
45
+
46
+ 18. Add runnable top-level bootstrap examples/scripts for Deno, Node, Bun, and browser worker hosts. (done)
47
+ Goal:
48
+ - one small checked-in startup entrypoint per concrete host adapter
49
+ - direct use of `workspace.json` plus the new host adapter surfaces
50
+ - clear examples of how a host actually boots and stays up in each environment
51
+
52
+ 19. Add external package-manager and persistence integration beyond the local workspace catalog. (done)
53
+ Done:
54
+ - persisted `packageCatalog` / `installedPackages` normalization in workspace state
55
+ - `installWorkspacePackageReference(...)`, `updateWorkspacePackageReference(...)`, and `removeWorkspacePackageReference(...)`
56
+ - `app.installPackageReference(...)`, `app.updatePackageReference(...)`, and `app.removePackageReference(...)`
57
+ - generic `createCommandPackageManager(...)` adapter
58
+ - concrete `createNpmPackageManager(...)` adapter
59
+ - workspace tests for package-reference persistence and runtime refresh alignment
60
+
61
+ 20. Add one environment-neutral installed-flow startup entrypoint that can select the concrete host adapter from workspace/runtime metadata. (done)
62
+ Done:
63
+ - `resolveInstalledFlowAutoHostEngine(...)`
64
+ - `startInstalledFlowAutoHost(...)`
65
+ - dispatch to browser, Deno, Bun, and Node startup surfaces
66
+ - explicit starter injection hooks for tests and embedded hosts
67
+
68
+ 21. Add a checked-in auto-host bootstrap script that starts from `workspace.json` through the environment-neutral startup surface. (done)
69
+ Done:
70
+ - `examples/bootstrap/start-auto-host.mjs`
71
+ - direct startup from the checked-in `sdn-js-catalog-gateway/workspace.json`
72
+ - bootstrap coverage proving the example dispatches through `startInstalledFlowAutoHost(...)`
73
+
74
+ 22. Add a simple host CLI that boots one workspace through the auto-host surface. (done)
75
+ Done:
76
+ - `bin/sdn-flow-host.js`
77
+ - `--workspace` and optional `--engine` override support
78
+ - startup summary plus `SIGINT`/`SIGTERM` shutdown hooks
79
+ - CLI coverage for parsing, help text, and startup dispatch
80
+
81
+ 23. Publish root AGENTS guidance for module and flow builders. (done)
82
+ Done:
83
+ - root `AGENTS.md` in `space-data-module-sdk` for compliant module authors
84
+ - rewritten root `AGENTS.md` in `sdn-flow` for compliant flow and host authors
85
+ - explicit integration-test lists for module, bundle, compiler, startup, and environment changes
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DigitalArsenal.io, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,223 @@
1
+ # sdn-flow
2
+
3
+ `sdn-flow` composes signed modules into typed flow graphs,
4
+ compiles those graphs into deployable WebAssembly runtimes, and provides host
5
+ surfaces for loading, starting, and serving those flows.
6
+
7
+ It builds on `space-data-module-sdk`, which defines the module artifact,
8
+ single-file bundle, capability vocabulary, and module ABI. `sdn-flow` owns the
9
+ flow graph, compiled flow descriptors, hosted runtime planning, deployment
10
+ client, and installed-flow startup layers.
11
+
12
+ ## Project Scope
13
+
14
+ This repo covers the full flow path:
15
+
16
+ - normalized flow programs and runtime descriptors
17
+ - designer-facing graph inspection and requirement summaries
18
+ - compilation from flow graph to one generated C++ translation unit
19
+ - `emception`-backed compilation to one deployable `.wasm` runtime artifact
20
+ - deployment packaging built on canonical module auth and transport records
21
+ - hosted runtime planning for browser, JS server, Go-oriented, and vendor
22
+ runtime profiles
23
+ - installed-flow startup surfaces for browser, Deno, Bun, and Node hosts
24
+
25
+ ## Flow Artifact
26
+
27
+ A compiled flow is one runtime artifact with:
28
+
29
+ - WebAssembly bytes
30
+ - an embedded FlatBuffer manifest
31
+ - exported runtime, invocation, and descriptor symbols
32
+ - embedded signed module dependencies
33
+ - deployment metadata for local or remote rollout
34
+
35
+ The compiler path uses a native C++ source generator compiled to WebAssembly
36
+ plus an `emception`-compatible compiler adapter, so the flow graph always
37
+ converges on the same runtime artifact format.
38
+
39
+ ## Runtime Portability
40
+
41
+ Flows generated by `sdn-flow` are runtime-neutral compiled WebAssembly
42
+ artifacts. A host can run the output anywhere it can:
43
+
44
+ - instantiate WebAssembly
45
+ - read FlatBuffers
46
+ - implement the canonical module and flow ABI surface
47
+
48
+ That is the same runtime family already exercised in the companion
49
+ `flatbuffers/wasm` work:
50
+
51
+ - browser
52
+ - Node.js
53
+ - C#
54
+ - Go
55
+ - Java
56
+ - Kotlin
57
+ - Python
58
+ - Rust
59
+ - Swift
60
+
61
+ This repo currently ships turnkey startup surfaces for browser, Deno, Bun, and
62
+ Node, and it includes environment profiles for `go-sdn` and WasmEdge-style
63
+ hosts. Non-JS runtimes consume the same compiled artifact through the runtime,
64
+ invocation, and descriptor ABIs exposed from `src/host`.
65
+
66
+ ## Relationship To Module SDK
67
+
68
+ `space-data-module-sdk` is the canonical module contract:
69
+
70
+ - manifest schema and embedded manifest exports
71
+ - module capability vocabulary
72
+ - compliance rules
73
+ - module signing and transport envelopes
74
+ - `sds.bundle` single-file packaging
75
+ - module hostcall/import ABI
76
+
77
+ `sdn-flow` layers flow composition on top of that contract:
78
+
79
+ - program structure and graph normalization
80
+ - compiled flow runtime descriptor and invocation ABI
81
+ - dependency composition for multi-module runtimes
82
+ - deployment clients and hosted runtime plans
83
+ - startup surfaces for hosts that launch compiled flows
84
+
85
+ ## Install
86
+
87
+ ```bash
88
+ npm install sdn-flow
89
+ ```
90
+
91
+ ## Compile A Flow
92
+
93
+ ```js
94
+ import {
95
+ EmceptionCompilerAdapter,
96
+ SignedArtifactCatalog,
97
+ normalizeProgram,
98
+ } from "sdn-flow";
99
+
100
+ const program = normalizeProgram(flowJson);
101
+
102
+ const compiler = new EmceptionCompilerAdapter({
103
+ emception,
104
+ artifactCatalog: new SignedArtifactCatalog(),
105
+ manifestBuilder: async () => manifestBytes,
106
+ });
107
+
108
+ const artifact = await compiler.compile({
109
+ program,
110
+ });
111
+ ```
112
+
113
+ The compiled artifact carries the exported symbols needed by hosts to inspect
114
+ and run the flow without reinterpreting the source graph.
115
+
116
+ ## Start A Host
117
+
118
+ For JS-family hosts, the quickest path is a workspace file plus the
119
+ environment-neutral auto-host entrypoint:
120
+
121
+ ```js
122
+ import { startInstalledFlowAutoHost } from "sdn-flow";
123
+
124
+ await startInstalledFlowAutoHost({
125
+ workspacePath: "./workspace.json",
126
+ });
127
+ ```
128
+
129
+ For Node-based startup from the command line:
130
+
131
+ ```bash
132
+ sdn-flow-host --workspace ./workspace.json
133
+ sdn-flow-host --workspace ./workspace.json --engine node
134
+ ```
135
+
136
+ That CLI is a thin wrapper over `startInstalledFlowAutoHost(...)` with startup
137
+ summary output and `SIGINT` / `SIGTERM` shutdown handling.
138
+
139
+ ## Host Surfaces
140
+
141
+ The host package is organized around a few layers:
142
+
143
+ - `createInstalledFlowHost(...)`
144
+ Discover or register plugin packages, load a program, and start the runtime.
145
+ - `createInstalledFlowService(...)`
146
+ Keep timer and HTTP-trigger services running for a loaded flow.
147
+ - `createInstalledFlowApp(...)`
148
+ Boot from `workspace.json`, persist startup state, and refresh the runtime
149
+ against workspace changes.
150
+ - `startInstalledFlowAutoHost(...)`
151
+ Select the matching startup surface for browser, Deno, Bun, or Node from
152
+ workspace metadata.
153
+ - `createNpmPackageManager(...)`
154
+ Tie workspace package references to real install, update, and remove flows.
155
+
156
+ For hosts that load compiled artifacts directly, `bindCompiledRuntimeAbi(...)`,
157
+ `bindCompiledInvocationAbi(...)`, and `bindCompiledDescriptorAbi(...)` expose
158
+ the low-level contract used to inspect and execute a compiled flow artifact.
159
+
160
+ ## Runtime Families
161
+
162
+ | Layer | Coverage |
163
+ |---|---|
164
+ | Compiled flow artifact | Browser, Node.js, C#, Go, Java, Kotlin, Python, Rust, Swift |
165
+ | Turnkey startup surfaces in this repo | Browser, Deno, Bun, Node |
166
+ | Checked-in environment profiles | `sdn-js`, `go-sdn`, WasmEdge-style vendor profile |
167
+
168
+ The built-in host planners focus on the engines exercised by the current
169
+ examples and startup helpers. The compiled flow artifact itself stays portable
170
+ across the broader runtime set above.
171
+
172
+ ## Examples
173
+
174
+ - [Bootstrap Examples](./examples/bootstrap/README.md)
175
+ - [Environment Demos](./examples/environments/README.md)
176
+ - [Single-Plugin Flow](./examples/flows/single-plugin-flow.json)
177
+ - [Field-Protected Catalog Entry Flow](./examples/flows/field-protected-catalog-entry/README.md)
178
+ - [ISS Proximity OEM Flow](./examples/flows/iss-proximity-oem/README.md)
179
+
180
+ Notable environment profiles:
181
+
182
+ - [`sdn-js-catalog-gateway`](./examples/environments/sdn-js-catalog-gateway/README.md)
183
+ Shared JS-host profile with Deno as the preferred single-file deployment
184
+ target.
185
+ - [`go-sdn-omm-service`](./examples/environments/go-sdn-omm-service/README.md)
186
+ Go-oriented service deployment profile.
187
+ - [`wasmedge-udp-spooler`](./examples/environments/wasmedge-udp-spooler/README.md)
188
+ Vendor-profile example for extension-backed UDP and filesystem handling.
189
+
190
+ ## Package Surface
191
+
192
+ - `sdn-flow`
193
+ - `sdn-flow/runtime`
194
+ - `sdn-flow/designer`
195
+ - `sdn-flow/host`
196
+ - `sdn-flow/auth`
197
+ - `sdn-flow/transport`
198
+ - `sdn-flow/deploy`
199
+ - `sdn-flow/compiler`
200
+ - `sdn-flow/compliance`
201
+
202
+ ## Documentation
203
+
204
+ - [Architecture](./docs/ARCHITECTURE.md)
205
+ - [Host Capability Model](./docs/HOST_CAPABILITY_MODEL.md)
206
+ - [Plugin Architecture](./docs/PLUGIN_ARCHITECTURE.md)
207
+ - [Plugin Manifest](./docs/PLUGIN_MANIFEST.md)
208
+ - [Compatibility Model](./docs/PLUGIN_COMPATIBILITY.md)
209
+ - [Plugin Compliance Checks](./docs/PLUGIN_COMPLIANCE_CHECKS.md)
210
+
211
+ ## Development
212
+
213
+ ```bash
214
+ npm install
215
+ npm test
216
+ ```
217
+
218
+ Node.js `>=20` is required. The compile path uses `../emception` for generated
219
+ C++ to WebAssembly builds.
220
+
221
+ ## License
222
+
223
+ [MIT](./LICENSE)
@@ -0,0 +1,169 @@
1
+ #!/usr/bin/env node
2
+ import path from "node:path";
3
+ import process from "node:process";
4
+
5
+ import { startInstalledFlowAutoHost } from "../src/index.js";
6
+
7
+ function normalizeString(value, fallback = null) {
8
+ if (typeof value !== "string") {
9
+ return fallback;
10
+ }
11
+ const normalized = value.trim();
12
+ return normalized.length > 0 ? normalized : fallback;
13
+ }
14
+
15
+ export function formatSdnFlowHostUsage() {
16
+ return [
17
+ "Usage: sdn-flow-host --workspace <workspace.json> [--engine <engine>]",
18
+ "",
19
+ "Options:",
20
+ " -w, --workspace <path> Path to the installed-flow workspace.json file.",
21
+ " -e, --engine <engine> Optional runtime override: node, deno, bun, browser.",
22
+ " -h, --help Show this help text.",
23
+ ].join("\n");
24
+ }
25
+
26
+ export function parseSdnFlowHostCliArgs(argv = []) {
27
+ const args = Array.isArray(argv) ? [...argv] : [];
28
+ let workspacePath = null;
29
+ let engine = null;
30
+ let help = false;
31
+
32
+ for (let index = 0; index < args.length; index += 1) {
33
+ const token = args[index];
34
+ if (token === "-h" || token === "--help") {
35
+ help = true;
36
+ continue;
37
+ }
38
+ if (token === "-w" || token === "--workspace") {
39
+ workspacePath = normalizeString(args[index + 1], null);
40
+ index += 1;
41
+ continue;
42
+ }
43
+ if (token === "-e" || token === "--engine") {
44
+ engine = normalizeString(args[index + 1], null);
45
+ index += 1;
46
+ continue;
47
+ }
48
+ if (!token.startsWith("-") && workspacePath === null) {
49
+ workspacePath = normalizeString(token, null);
50
+ continue;
51
+ }
52
+ throw new Error(`Unknown argument: ${token}`);
53
+ }
54
+
55
+ return {
56
+ workspacePath,
57
+ engine,
58
+ help,
59
+ };
60
+ }
61
+
62
+ function summarizeStartedHost(host) {
63
+ const startupSummary = host?.startup?.workspace ?? null;
64
+ const listenerCount = Array.isArray(host?.listeners)
65
+ ? host.listeners.length
66
+ : Array.isArray(host?.bindingContexts)
67
+ ? host.bindingContexts.length
68
+ : 0;
69
+
70
+ return {
71
+ workspaceId:
72
+ startupSummary?.workspaceId ?? host?.app?.getSummary?.().workspaceId ?? null,
73
+ programId:
74
+ startupSummary?.programId ?? host?.app?.getSummary?.().programId ?? null,
75
+ engine: startupSummary?.engine ?? host?.app?.getSummary?.().engine ?? null,
76
+ listenerCount,
77
+ };
78
+ }
79
+
80
+ function installShutdownHooks(host, options = {}) {
81
+ if (
82
+ options.registerSignalHandlers === false ||
83
+ typeof host?.stop !== "function" ||
84
+ typeof process.on !== "function"
85
+ ) {
86
+ return;
87
+ }
88
+
89
+ let stopping = false;
90
+ const stopHost = async () => {
91
+ if (stopping) {
92
+ return;
93
+ }
94
+ stopping = true;
95
+ await host.stop();
96
+ if (options.exitProcess !== false) {
97
+ process.exit(0);
98
+ }
99
+ };
100
+
101
+ process.on("SIGINT", () => {
102
+ void stopHost();
103
+ });
104
+ process.on("SIGTERM", () => {
105
+ void stopHost();
106
+ });
107
+ }
108
+
109
+ export async function runSdnFlowHostCli(argv = process.argv.slice(2), options = {}) {
110
+ const parsed = parseSdnFlowHostCliArgs(argv);
111
+ const log = options.log ?? console.log.bind(console);
112
+
113
+ if (parsed.help) {
114
+ log(formatSdnFlowHostUsage());
115
+ return {
116
+ kind: "help",
117
+ args: parsed,
118
+ };
119
+ }
120
+
121
+ if (!parsed.workspacePath) {
122
+ throw new Error("sdn-flow-host requires --workspace <workspace.json>.");
123
+ }
124
+
125
+ const resolvedWorkspacePath = path.resolve(parsed.workspacePath);
126
+ const host = await (
127
+ options.startInstalledFlowAutoHost ?? startInstalledFlowAutoHost
128
+ )({
129
+ ...options.startOptions,
130
+ workspacePath: resolvedWorkspacePath,
131
+ ...(parsed.engine ? { engine: parsed.engine } : {}),
132
+ });
133
+ const summary = summarizeStartedHost(host);
134
+
135
+ if (options.quiet !== true) {
136
+ log(
137
+ `Started ${summary.workspaceId ?? "installed-flow workspace"} (${summary.engine ?? "unknown"}) from ${resolvedWorkspacePath}${
138
+ summary.listenerCount > 0
139
+ ? ` with ${summary.listenerCount} listener${summary.listenerCount === 1 ? "" : "s"}`
140
+ : ""
141
+ }.`,
142
+ );
143
+ }
144
+
145
+ installShutdownHooks(host, options);
146
+
147
+ return {
148
+ kind: "started",
149
+ args: {
150
+ ...parsed,
151
+ workspacePath: resolvedWorkspacePath,
152
+ },
153
+ host,
154
+ summary,
155
+ };
156
+ }
157
+
158
+ if (import.meta.main) {
159
+ try {
160
+ await runSdnFlowHostCli();
161
+ } catch (error) {
162
+ console.error(
163
+ typeof error?.message === "string" ? error.message : String(error),
164
+ );
165
+ console.error("");
166
+ console.error(formatSdnFlowHostUsage());
167
+ process.exit(1);
168
+ }
169
+ }
package/docs/.nojekyll ADDED
File without changes