create-aiaui-module 0.1.0 → 0.1.2-TEST

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 (33) hide show
  1. package/README.md +84 -17
  2. package/bin/create-aiaui-module.js +86 -22
  3. package/package.json +23 -6
  4. package/runtime/__init__.py +71 -28
  5. package/runtime/_runtime/host.py +7 -15
  6. package/templates/HelloWorld/README.md +9 -1
  7. package/templates/JobInformation/CUSTOMIZATION.md +98 -148
  8. package/templates/JobInformation/README.md +17 -3
  9. package/templates/JobInformation/api.py +64 -20
  10. package/templates/JobInformation/resources/dist/modules.js +1548 -954
  11. package/templates/JobInformation/resources/src/contract.ts +109 -37
  12. package/templates/JobInformation/resources/src/customization.ts +15 -10
  13. package/templates/JobInformation/resources/src/defaults/DefaultPanel.vue +122 -15
  14. package/templates/JobInformation/resources/src/defaults/Icons.vue +49 -0
  15. package/templates/JobInformation/resources/src/defaults/InputCarriers.vue +12 -9
  16. package/templates/JobInformation/resources/src/defaults/JobBottom.vue +32 -0
  17. package/templates/JobInformation/resources/src/defaults/JobCard.vue +48 -0
  18. package/templates/JobInformation/resources/src/defaults/JobHeader.vue +41 -0
  19. package/templates/JobInformation/resources/src/defaults/JobMiddle.vue +41 -0
  20. package/templates/JobInformation/resources/src/defaults/JobName.vue +9 -0
  21. package/templates/JobInformation/resources/src/defaults/LotId.vue +14 -0
  22. package/templates/JobInformation/resources/src/defaults/OutputCarriers.vue +15 -8
  23. package/templates/JobInformation/resources/src/defaults/Recipe.vue +6 -12
  24. package/templates/JobInformation/resources/src/defaults/Status.vue +29 -0
  25. package/templates/JobInformation/resources/src/defaults/Timeline.vue +57 -14
  26. package/templates/JobInformation/resources/src/defaults/board-data.ts +36 -0
  27. package/templates/JobInformation/resources/src/defaults/config-defaults.ts +36 -0
  28. package/templates/JobInformation/resources/src/defaults/format.ts +51 -0
  29. package/templates/JobInformation/resources/src/defaults/slots.ts +16 -6
  30. package/templates/JobInformation/resources/src/defaults/styles.css +576 -47
  31. package/templates/JobInformation/resources/src/index.ts +10 -18
  32. package/templates/JobInformation/resources/src/bus.ts +0 -38
  33. package/templates/JobInformation/resources/src/defaults/Header.vue +0 -12
package/README.md CHANGED
@@ -1,35 +1,102 @@
1
- # AIAUI module templates
1
+ # create-aiaui-module
2
2
 
3
- This directory is the source of truth for module starter code. Each subdirectory of `templates/` is named after the generated module ID and contains `contractVersion`, a README, and the module's editable files. `api.py` is optional. Frontend templates include Vue components plus a slot customization contract, a `resources/src/index.ts` bridge with `mount` and `unmount`, and a prebuilt `resources/dist/`.
3
+ 在 AIAUI Instance 根目录一键生成可编辑的模块目录(FastAPI 后端路由 + Vue 前端)。
4
+ 通过 `npx` 运行,无需全局安装、无需克隆仓库,运行时无外部依赖(除 Instance
5
+ 侧已声明的 Python 依赖)。
4
6
 
5
- ## Use with npx (no global install)
7
+ | 步骤 | 命令 | 说明 |
8
+ | --- | --- | --- |
9
+ | 查看可用模块 | `npx create-aiaui-module@latest --list` | 列出模块 ID 与 contractVersion |
10
+ | 生成模块 | `npx create-aiaui-module@latest JobInformation` | 在实例根目录生成 `PyAIAUI/JobInformation` |
11
+ | 机器可读 | `npx create-aiaui-module@latest JobInformation --json` | 生成结果输出 JSON(接 CI) |
6
12
 
7
- From an Instance root containing `Config/`:
13
+ ## 环境要求
14
+
15
+ - 执行生成器的机器:Node.js >= 20
16
+ - Instance 运行侧:Python + `fastapi`、`uvicorn`(Instance 侧自备,`pip install fastapi uvicorn`)
17
+
18
+ ## 快速开始
19
+
20
+ 在 Instance 根目录(含 `Config/` 的目录)下执行:
8
21
 
9
22
  ```bash
10
23
  npx create-aiaui-module@latest JobInformation
11
- npx create-aiaui-module@latest --list # show available template IDs
12
- npx create-aiaui-module@latest JobInformation --json # machine-readable summary
13
24
  ```
14
25
 
15
- `npx` fetches the package on demand; nothing needs to be installed globally.
16
- Use `npx create-aiaui-module@<version>` to pin a specific release.
26
+ 生成后 `PyAIAUI/JobInformation/` 与 `PyAIAUI/_runtime/` 就位,`Config/AIAUIProtocol.json` 中登记该模块(默认 `enabled: false`、`port: null`)。
27
+
28
+ 启用前需要调整的配置:
29
+
30
+ ```json
31
+ {
32
+ "schemaVersion": 1,
33
+ "port": 18901,
34
+ "modules": [
35
+ { "name": "JobInformation", "enabled": true }
36
+ ]
37
+ }
38
+ ```
39
+
40
+ 把 Instance 启动/关闭流程接上(生成后 `PyAIAUI` 目录里就是这段代码的宿主实现):
41
+
42
+ ```python
43
+ from PyAIAUI import AIAUI
44
+
45
+ # 业务对象就绪后
46
+ aiaui = AIAUI.start(services={"job_repository": repo})
47
+ # aiaui 为 None 表示没有任何模块启用
48
+
49
+ # ... Instance 正常运行 ...
50
+
51
+ # 关闭前
52
+ if aiaui is not None:
53
+ aiaui.stop()
54
+ ```
55
+
56
+ `AIAUI.start()` 幂等(重复调用返回同一 handle),一个 handle 托管本 Instance 全部启用模块,共用一个 loopback 端口。后端接口由模块自己的 `api.py` 里的 `create_api(context)` 提供,`context.services` 就是上面注入的业务对象。
57
+
58
+ ## 生成结果
59
+
60
+ ```
61
+ <Instance root>/
62
+ ├── Config/AIAUIProtocol.json # 运行协议:端口 + 模块启停(生成器只登记,不配置)
63
+ └── PyAIAUI/
64
+ ├── __init__.py # 入口:AIAUI 类(AIAUI.start / aiaui.stop)
65
+ ├── JobInformation/ # 模块目录,目录名 == 模块 ID
66
+ │ ├── api.py # 后端契约:create_api(context) -> APIRouter
67
+ │ ├── resources/src/ # Vue 前端源码
68
+ │ ├── resources/dist/ # 预构建产物(开箱即跑,改源码后 npm run build 重出)
69
+ │ └── {README.md, CUSTOMIZATION.md}# 模块说明与定制手册
70
+ └── _runtime/ # 内嵌模块宿主(零外部依赖)
71
+ ```
72
+
73
+ 生成器不选端口、不装依赖、不启动进程、不覆盖已存在的模块目录;无效的既有协议会被拒绝且不留残留。
74
+
75
+ ## 内置模块清单
17
76
 
18
- ## Local use
77
+ | 模块 ID | 说明 | 组成 |
78
+ | --- | --- | --- |
79
+ | `JobInformation` | 工单 / 载具 / 工序信息面板 | FastAPI 后端 + Vue 前端;内置 `header`、`inputCarriers`、`outputCarriers`、`timeline`、`bottom`(含嵌套的 `recipe`)可替换槽位;三级定制(默认 / 局部替换 / 全量自定义) |
80
+ | `HelloWorld` | 最小前端模块示例 | 自定义元素;验证挂载链路,无后端 |
81
+ 自定义通过 `resources/src/customization.ts` 完成,槽位通信通过 `context.bus`(事件 / expose+peer),详见生成目录里的 `CUSTOMIZATION.md`。
19
82
 
20
- From an Instance root in a checkout:
83
+ ## UIService 集成
21
84
 
22
85
  ```bash
23
- node /path/to/ServerManageForLiunx/UIModulesTemplate/bin/create-aiaui-module.js --list
24
- node /path/to/ServerManageForLiunx/UIModulesTemplate/bin/create-aiaui-module.js JobInformation
86
+ npm i -g create-aiaui-module
87
+ export AIA_UI_MODULE_TEMPLATE_ROOT="$(npm root -g)/create-aiaui-module"
25
88
  ```
26
89
 
27
- From another directory, pass `--instance-root /path/to/Instance`. Existing `PyAIAUI/<ModuleId>` directories are never overwritten. Generation creates `Config/AIAUIProtocol.json` when absent, with `port: null` and a disabled module entry. If the protocol already exists, it appends a disabled entry without changing its port or other settings. It does not choose a port, install dependencies, or start a process. An invalid existing protocol is rejected without leaving a new module directory.
90
+ 此后 UIService 调用包内 `bin/create-aiaui-module.js`(与 `npx` 行为一致),或在业务机上直接 `npx`。
28
91
 
29
- To test the npm snapshot locally, run `npm pack --pack-destination /tmp` here, then use `npm exec --offline --package=/tmp/create-aiaui-module-0.1.0.tgz -- create-aiaui-module --list`.
92
+ ## 常用命令
30
93
 
31
- UIService invokes this same script. A deployed UIService needs Node.js (>= 20) and `AIA_UI_MODULE_TEMPLATE_ROOT` pointing to the installed package directory (for example the package installed under `npm root -g`).
94
+ - `npx create-aiaui-module@latest --list` — 查看模板
95
+ - `npx create-aiaui-module@latest <ModuleId>` — 生成(`--instance-root` 指定根目录)
96
+ - `npx create-aiaui-module@latest --json --list` — 机器可读
32
97
 
33
- ## Add a template
98
+ ## 维护者(发布新模板)
34
99
 
35
- Add `templates/<ModuleId>/` with a `contractVersion` file and a complete editable implementation. If it has a frontend, run `npm install` and `npm run build` from its `resources/` directory, then commit the updated `dist/`. Run `npm test` here and inspect `npm pack --dry-run --json` before packaging a new version.
100
+ - 新增模块:在 `templates/<ModuleId>/` 下放实现(含 `contractVersion`、`README.md`、`resources/`)
101
+ - 前端模板发布前在 `resources/` 跑 `npm install && npm run build`,把产物一起随包发布;发布自动执行 `verify-dist` 校验每个模板的 `contractVersion`、入口、`dist/` 产物
102
+ - 发版流程:`npm test` → 改 `package.json` 版本号 → `npm publish`
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import { readFileSync } from "node:fs";
2
3
  import path from "node:path";
3
4
  import { fileURLToPath } from "node:url";
4
5
 
@@ -9,6 +10,79 @@ import {
9
10
  } from "../src/generator.js";
10
11
 
11
12
  const packageRoot = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
13
+ const DIVIDER = "─".repeat(48);
14
+
15
+ function briefDescription(templateId) {
16
+ try {
17
+ const text = readFileSync(
18
+ path.join(packageRoot, "templates", templateId, "README.md"),
19
+ "utf8",
20
+ );
21
+ for (const line of text.split("\n")) {
22
+ const trimmed = line.trim();
23
+ if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("---"))
24
+ continue;
25
+ // Keep the first sentence so the list stays scannable.
26
+ const sentence = trimmed.split(/(?<=\.)\s([A-Z])/)[0];
27
+ const clipped = sentence.length > 120 ? `${sentence.slice(0, 117)}…` : sentence;
28
+ return clipped.length > 0 ? clipped : trimmed;
29
+ }
30
+ } catch {
31
+ /* template without README simply shows no description */
32
+ }
33
+ return "";
34
+ }
35
+
36
+ function printTemplateList(entries) {
37
+ console.log();
38
+ console.log("AIAUI 模块模板");
39
+ console.log(DIVIDER);
40
+ if (entries.length === 0) console.log(" (none)");
41
+ for (const entry of entries) {
42
+ console.log(` ${entry.id} · contract v${entry.contractVersion}`);
43
+ if (briefDescription(entry.id))
44
+ console.log(` ${briefDescription(entry.id)}`);
45
+ }
46
+ console.log(DIVIDER);
47
+ console.log("生成: npx create-aiaui-module <模块名> [--list] [--json]");
48
+ }
49
+
50
+ function printSuccess(result) {
51
+ console.log();
52
+ console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
53
+ console.log(" [AIAUI] 生成成功");
54
+ console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
55
+ console.log();
56
+ console.log(` ✅ 生成路径 ${result.path} (${result.filesCopied} 个文件)`);
57
+ console.log(` ✅ 协议配置 ${result.configPath} (enabled: ${result.enabled})`);
58
+ console.log();
59
+ console.log("⚠️ 配置调整(需要你手动做)");
60
+ console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
61
+ console.log(" 📌 修改 port 字段为 18901(或你选择的其他空闲端口)");
62
+ console.log(" 📌 将 enabled 改为 true");
63
+ console.log(" 📌 模块接入 Instance 启动流程后再启用");
64
+ console.log();
65
+ console.log("🔧 接下来要做:");
66
+ console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
67
+ console.log(" 1. 配置端口");
68
+ console.log(" 修改 AIAUIProtocol.json 中 port 字段为 18901(示例值)");
69
+ console.log(" 2. 配置模块 enable");
70
+ console.log(" 修改 AIAUIProtocol.json 中对应模块的 enabled 为 true");
71
+ console.log(" 3. 接入 Instance 启动流程");
72
+ console.log(" · 启动时: aiaui = AIAUI.start(services={...})");
73
+ console.log(" · 关闭时: aiaui.stop()");
74
+ console.log(" 4. 打开 UIClient 对应页面查看效果(JobInformation → /jobs)");
75
+ console.log();
76
+ console.log("❌ 没有做的(请确认)");
77
+ console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
78
+ console.log(" · 未启动服务(由 Instance 生命周期调用 AIAUI.start)");
79
+ console.log(" · 未安装依赖(模块运行需要 pip install fastapi uvicorn)");
80
+ console.log(" · 未选择/配置端口");
81
+ console.log(" · 未手动启用任何模块");
82
+ console.log();
83
+ console.log(" 初始化目录已就绪,按上面的顺序操作即可上线。");
84
+ console.log();
85
+ }
12
86
 
13
87
  function parseArgs(args) {
14
88
  const options = {
@@ -42,28 +116,18 @@ function parseArgs(args) {
42
116
 
43
117
  try {
44
118
  const options = parseArgs(process.argv.slice(2));
45
- const result = options.list
46
- ? await listTemplates(packageRoot)
47
- : await createModule({
48
- templateId: options.templateId,
49
- instanceRoot: options.instanceRoot,
50
- root: packageRoot,
51
- });
52
- if (options.json) console.log(JSON.stringify(result));
53
- else if (options.list)
54
- console.log(
55
- result
56
- .map((item) => `${item.id} (contract ${item.contractVersion})`)
57
- .join("\n"),
58
- );
59
- else {
60
- console.log(`Generated ${result.path} (${result.filesCopied} files)`);
61
- console.log(
62
- `Registered in ${result.configPath} (enabled: ${result.enabled}, port: ${result.port ?? "unset"})`,
63
- );
64
- console.log(
65
- "Next: call PyAIAUI.start(services={...}) / PyAIAUI.stop() from the Instance lifecycle, set a free port, then enable the module. Generation does not start it.",
66
- );
119
+ if (options.list) {
120
+ const entries = await listTemplates(packageRoot);
121
+ if (options.json) console.log(JSON.stringify(entries));
122
+ else printTemplateList(entries);
123
+ } else {
124
+ const result = await createModule({
125
+ templateId: options.templateId,
126
+ instanceRoot: options.instanceRoot,
127
+ root: packageRoot,
128
+ });
129
+ if (options.json) console.log(JSON.stringify(result));
130
+ else printSuccess(result);
67
131
  }
68
132
  } catch (error) {
69
133
  const code =
package/package.json CHANGED
@@ -1,12 +1,29 @@
1
1
  {
2
2
  "name": "create-aiaui-module",
3
- "version": "0.1.0",
4
- "description": "Scaffold AIAUI Instance modules (FastAPI router + editable Vue frontend) via npx",
5
- "keywords": ["aiaui", "scaffolding", "generator", "vue", "fastapi", "module", "instance"],
3
+ "version": "0.1.2-TEST",
4
+ "description": "AIAUI Instance module scaffolder: generate editable module (FastAPI + Vue frontend) with one npx command",
5
+ "keywords": [
6
+ "aiaui",
7
+ "scaffolding",
8
+ "generator",
9
+ "vue",
10
+ "fastapi",
11
+ "module",
12
+ "instance"
13
+ ],
6
14
  "type": "module",
7
- "bin": { "create-aiaui-module": "./bin/create-aiaui-module.js" },
8
- "files": ["bin", "src", "runtime", "templates"],
9
- "engines": { "node": ">=20" },
15
+ "bin": {
16
+ "create-aiaui-module": "./bin/create-aiaui-module.js"
17
+ },
18
+ "files": [
19
+ "bin",
20
+ "src",
21
+ "runtime",
22
+ "templates"
23
+ ],
24
+ "engines": {
25
+ "node": ">=20"
26
+ },
10
27
  "scripts": {
11
28
  "test": "node tests/generator.test.js",
12
29
  "verify-dist": "node scripts/verify-dist.mjs",
@@ -1,39 +1,82 @@
1
1
  """Instance-side entry point for enabled AIAUI modules.
2
2
 
3
- The owning Instance calls start() after its business objects are ready and
4
- stop() before those objects are torn down. Generated modules remain editable;
5
- this file only delegates discovery and hosting to the inlined ``_runtime``
6
- package (no external ``aiaui`` dependency is required).
3
+ The owning Instance calls ``AIAUI.start(services={...})`` once its business
4
+ objects are ready and ``aiaui.stop()`` during shutdown. Generated modules
5
+ remain editable; this file only delegates discovery and hosting to the
6
+ inlined ``_runtime`` package (no external ``aiaui`` dependency is required).
7
+
8
+ Typical use::
9
+
10
+ from PyAIAUI import AIAUI
11
+
12
+ aiaui = AIAUI.start(services={"job_repository": repo})
13
+ # ... run the Instance ...
14
+ aiaui.stop()
15
+
16
+ ``AIAUI.start()`` returns ``None`` when no modules are enabled in
17
+ ``Config/AIAUIProtocol.json``.
7
18
  """
8
19
 
9
20
  from pathlib import Path
10
21
 
11
22
  from ._runtime import attach
12
23
 
13
- _host = None
14
24
 
25
+ class AIAUI:
26
+ """Host handle for one Instance's enabled AIAUI modules.
15
27
 
16
- def start(*, services=None, logger=None):
17
- """Start the enabled modules for this Instance, returning their host.
18
-
19
- Pass the Instance's live business objects through ``services`` so module
20
- backends (``create_api(context)``) can reach them via ``context.services``.
21
- Returns None when no modules are enabled in ``Config/AIAUIProtocol.json``.
28
+ One handle hosts every enabled module of the Instance on a single
29
+ loopback HTTP port (see ``Config/AIAUIProtocol.json``). ``start()`` is
30
+ idempotent per Instance: a second call returns the same handle while the
31
+ host lives, and ``stop()`` tears everything down.
22
32
  """
23
- global _host
24
- if _host is not None and not _host.closed:
25
- return _host
26
- _host = attach(
27
- Path(__file__).resolve().parent.parent,
28
- services=services,
29
- logger=logger,
30
- )
31
- return _host
32
-
33
-
34
- def stop():
35
- """Stop the host when the Instance's business objects are shutting down."""
36
- global _host
37
- host, _host = _host, None
38
- if host is not None:
39
- host.close()
33
+
34
+ _current: "AIAUI | None" = None
35
+
36
+ def __init__(self, host):
37
+ self._host = host
38
+ self.closed = False
39
+
40
+ @classmethod
41
+ def start(cls, *, services=None, logger=None) -> "AIAUI | None":
42
+ """Start this Instance's enabled modules and return the handle.
43
+
44
+ ``services`` are the Instance's live business objects; every module
45
+ backend (``create_api(context)``) reaches them through
46
+ ``context.services``. Returns ``None`` when no modules are enabled.
47
+
48
+ ``start()`` never selects a port, installs dependencies, or starts a
49
+ network socket outside the loopback interface; the port comes from
50
+ the Instance's ``Config/AIAUIProtocol.json``.
51
+ """
52
+ current = cls._current
53
+ if current is not None and not current.closed:
54
+ return current
55
+ host = attach(
56
+ Path(__file__).resolve().parent.parent,
57
+ services=services,
58
+ logger=logger,
59
+ )
60
+ if host is None:
61
+ return None
62
+ handle = cls(host)
63
+ cls._current = handle
64
+ return handle
65
+
66
+ @property
67
+ def host(self):
68
+ """The underlying loopback host, or ``None`` after ``stop()``.
69
+
70
+ Exposed for advanced inspection only; normal code should use
71
+ ``stop()`` and let this handle the lifecycle.
72
+ """
73
+ return None if self.closed else self._host
74
+
75
+ def stop(self) -> None:
76
+ """Stop every module hosted for this Instance (usually at shutdown)."""
77
+ if self.closed:
78
+ return
79
+ self.closed = True
80
+ self._host.close()
81
+ if AIAUI._current is self:
82
+ AIAUI._current = None
@@ -12,9 +12,6 @@ OWNER = "aiaui-proxy"
12
12
 
13
13
  class ProxyHost:
14
14
  def __init__(self, instance_root: Path, port: int, modules: list[ModuleDescriptor], context):
15
- import fcntl
16
- import os
17
-
18
15
  import uvicorn
19
16
  from fastapi import FastAPI
20
17
 
@@ -24,17 +21,13 @@ class ProxyHost:
24
21
  self.closed = False
25
22
  self.thread: threading.Thread | None = None
26
23
 
27
- config_dir = self.root / "Config"
28
- config_dir.mkdir(parents=True, exist_ok=True)
29
- lock_path = config_dir / ".aiaui.host.lock"
30
- descriptor = os.open(lock_path, os.O_CREAT | os.O_RDWR | os.O_NOFOLLOW, 0o600)
31
- self._lock_file = os.fdopen(descriptor, "a")
32
- try:
33
- fcntl.flock(self._lock_file, fcntl.LOCK_EX | fcntl.LOCK_NB)
34
- except BlockingIOError as error:
35
- self._lock_file.close()
36
- raise ValueError("Another process already hosts this Instance") from error
37
-
24
+ # No file lock: the loopback bind in `name:port` (the protocol's port)
25
+ # already enforces "one host per Instance". A second host for the
26
+ # same Instance fails fast with a uvicorn bind error. If two hosts
27
+ # for the same Instance ever end up on different ports, the second
28
+ # one silently serves alongside the first; this is an edge case the
29
+ # caller avoids by keeping AIAUIProtocol.json as the single source
30
+ # of truth.
38
31
  self._app = FastAPI(title=f"AIAUI modules for {self.root.name}")
39
32
  for module in modules:
40
33
  router = load_api(module, context)
@@ -85,4 +78,3 @@ class ProxyHost:
85
78
  if self.thread is not None:
86
79
  self.server.should_exit = True
87
80
  self.thread.join(timeout=5)
88
- self._lock_file.close()
@@ -2,4 +2,12 @@
2
2
 
3
3
  This frontend-only template shows a minimal AIAUI module. Edit `resources/src/Hello.ce.vue`, then run `npm install` and `npm run build` inside `resources/` to refresh the prebuilt file. `resources/src/index.ts` remains the module entry. There is intentionally no `api.py`.
4
4
 
5
- Generation creates `PyAIAUI/HelloWorld/` and registers it as disabled in `Config/AIAUIProtocol.json`. Set a port and enable it after integrating the module host.
5
+ Generation creates `PyAIAUI/HelloWorld/` and registers it as disabled in `Config/AIAUIProtocol.json`. Set a port and enable it after integrating the module host:
6
+
7
+ ```python
8
+ from PyAIAUI import AIAUI
9
+
10
+ aiaui = AIAUI.start(services={...})
11
+ # ... later ...
12
+ aiaui.stop()
13
+ ```