dominds 1.4.2 → 1.5.2
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/README.md +24 -0
- package/README.zh.md +24 -0
- package/dist/apps/app-json.js +38 -3
- package/dist/apps/dialog-run-controls.js +4 -0
- package/dist/apps/enabled-apps.js +8 -1
- package/dist/apps/installed-file.js +207 -0
- package/dist/apps/run-app-json.js +6 -6
- package/dist/apps/runtime-port.js +91 -0
- package/dist/apps/runtime.js +316 -68
- package/dist/apps-host/client.js +153 -3
- package/dist/apps-host/host.js +339 -2
- package/dist/apps-host/ipc-types.js +215 -30
- package/dist/cli/install.js +21 -1
- package/dist/dialog-fork.js +609 -0
- package/dist/dialog.js +2 -2
- package/dist/docs/agent-priming.md +38 -0
- package/dist/docs/agent-priming.zh.md +34 -0
- package/dist/docs/app-constitution.md +153 -2
- package/dist/docs/app-constitution.zh.md +153 -2
- package/dist/docs/dialog-persistence.md +31 -0
- package/dist/docs/dialog-persistence.zh.md +31 -0
- package/dist/docs/dialog-system.md +29 -0
- package/dist/docs/dialog-system.zh.md +29 -0
- package/dist/docs/kernel-app-architecture.md +286 -0
- package/dist/docs/kernel-app-architecture.zh.md +285 -0
- package/dist/llm/defaults.yaml +16 -0
- package/dist/llm/driver-entry.js +28 -0
- package/dist/llm/driver-v2/context-health.js +121 -0
- package/dist/llm/driver-v2/context.js +56 -0
- package/dist/llm/driver-v2/core.js +1545 -0
- package/dist/llm/driver-v2/index.js +26 -0
- package/dist/llm/driver-v2/orchestrator.js +158 -0
- package/dist/llm/driver-v2/policy.js +129 -0
- package/dist/llm/driver-v2/restore-dialog-hierarchy.js +73 -0
- package/dist/llm/driver-v2/round.js +366 -0
- package/dist/llm/driver-v2/runtime-utils.js +365 -0
- package/dist/llm/driver-v2/saying-events.js +20 -0
- package/dist/llm/driver-v2/subdialog-txn.js +42 -0
- package/dist/llm/driver-v2/supdialog-response.js +400 -0
- package/dist/llm/driver-v2/tellask-bridge.js +1148 -0
- package/dist/llm/driver-v2/types.js +10 -0
- package/dist/llm/driver-v2-ref-only/context-health.js +121 -0
- package/dist/llm/driver-v2-ref-only/context.js +17 -0
- package/dist/llm/driver-v2-ref-only/core.js +1710 -0
- package/dist/llm/driver-v2-ref-only/index.js +26 -0
- package/dist/llm/driver-v2-ref-only/orchestrator.js +158 -0
- package/dist/llm/driver-v2-ref-only/policy.js +129 -0
- package/dist/llm/driver-v2-ref-only/restore-dialog-hierarchy.js +73 -0
- package/dist/llm/driver-v2-ref-only/round.js +366 -0
- package/dist/llm/driver-v2-ref-only/runtime-utils.js +473 -0
- package/dist/llm/driver-v2-ref-only/saying-events.js +18 -0
- package/dist/llm/driver-v2-ref-only/subdialog-txn.js +42 -0
- package/dist/llm/driver-v2-ref-only/supdialog-response.js +453 -0
- package/dist/llm/driver-v2-ref-only/tellask-bridge.js +1178 -0
- package/dist/llm/driver-v2-ref-only/types.js +10 -0
- package/dist/llm/gen/anthropic.js +68 -15
- package/dist/llm/gen/codex.js +59 -10
- package/dist/llm/gen/openai-compatible.js +38 -9
- package/dist/llm/gen/openai.js +58 -11
- package/dist/llm/gen/tool-output-limit.js +50 -0
- package/dist/llm/kernel-driver/subdialog.js +23 -12
- package/dist/llm/kernel-driver/tellask-special.js +20 -4
- package/dist/minds/load.js +7 -0
- package/dist/persistence.js +216 -30
- package/dist/priming.js +171 -18
- package/dist/server/api-routes.js +82 -0
- package/dist/server/setup-routes.js +15 -0
- package/dist/shared/types/storage.js +77 -0
- package/dist/static/assets/{_basePickBy-B2o4z1Hf.js → _basePickBy-B-A5XrWM.js} +3 -3
- package/dist/static/assets/{_basePickBy-B2o4z1Hf.js.map → _basePickBy-B-A5XrWM.js.map} +1 -1
- package/dist/static/assets/{_baseUniq-CLmcxjdl.js → _baseUniq-BANLb0cu.js} +2 -2
- package/dist/static/assets/{_baseUniq-CLmcxjdl.js.map → _baseUniq-BANLb0cu.js.map} +1 -1
- package/dist/static/assets/{arc-CymD_KN7.js → arc-CYZYnojf.js} +2 -2
- package/dist/static/assets/{arc-CymD_KN7.js.map → arc-CYZYnojf.js.map} +1 -1
- package/dist/static/assets/{architectureDiagram-VXUJARFQ-DJQfSJUH.js → architectureDiagram-VXUJARFQ-Cxf4pmYG.js} +7 -7
- package/dist/static/assets/{architectureDiagram-VXUJARFQ-DJQfSJUH.js.map → architectureDiagram-VXUJARFQ-Cxf4pmYG.js.map} +1 -1
- package/dist/static/assets/{blockDiagram-VD42YOAC-pHVz60D0.js → blockDiagram-VD42YOAC-wvs0G30c.js} +7 -7
- package/dist/static/assets/{blockDiagram-VD42YOAC-pHVz60D0.js.map → blockDiagram-VD42YOAC-wvs0G30c.js.map} +1 -1
- package/dist/static/assets/{c4Diagram-YG6GDRKO-B0WnCfAT.js → c4Diagram-YG6GDRKO-BKFNexn4.js} +3 -3
- package/dist/static/assets/{c4Diagram-YG6GDRKO-B0WnCfAT.js.map → c4Diagram-YG6GDRKO-BKFNexn4.js.map} +1 -1
- package/dist/static/assets/{channel-CX9BlKil.js → channel-_1qpxJWy.js} +2 -2
- package/dist/static/assets/{channel-CX9BlKil.js.map → channel-_1qpxJWy.js.map} +1 -1
- package/dist/static/assets/{chunk-4BX2VUAB-lXArRj3o.js → chunk-4BX2VUAB-BIdC0phm.js} +2 -2
- package/dist/static/assets/{chunk-4BX2VUAB-lXArRj3o.js.map → chunk-4BX2VUAB-BIdC0phm.js.map} +1 -1
- package/dist/static/assets/{chunk-55IACEB6-CdqwynH9.js → chunk-55IACEB6-BNvGenQ9.js} +2 -2
- package/dist/static/assets/{chunk-55IACEB6-CdqwynH9.js.map → chunk-55IACEB6-BNvGenQ9.js.map} +1 -1
- package/dist/static/assets/{chunk-B4BG7PRW-Y-uXcJst.js → chunk-B4BG7PRW-jmf-1Wv7.js} +5 -5
- package/dist/static/assets/{chunk-B4BG7PRW-Y-uXcJst.js.map → chunk-B4BG7PRW-jmf-1Wv7.js.map} +1 -1
- package/dist/static/assets/{chunk-DI55MBZ5-C5xSbRST.js → chunk-DI55MBZ5-nmEmcikR.js} +4 -4
- package/dist/static/assets/{chunk-DI55MBZ5-C5xSbRST.js.map → chunk-DI55MBZ5-nmEmcikR.js.map} +1 -1
- package/dist/static/assets/{chunk-FMBD7UC4-5uefwCjI.js → chunk-FMBD7UC4-kGysaq_j.js} +2 -2
- package/dist/static/assets/{chunk-FMBD7UC4-5uefwCjI.js.map → chunk-FMBD7UC4-kGysaq_j.js.map} +1 -1
- package/dist/static/assets/{chunk-QN33PNHL-DzWVcvpI.js → chunk-QN33PNHL-8JwMLFIJ.js} +2 -2
- package/dist/static/assets/{chunk-QN33PNHL-DzWVcvpI.js.map → chunk-QN33PNHL-8JwMLFIJ.js.map} +1 -1
- package/dist/static/assets/{chunk-QZHKN3VN-BrrvAZdP.js → chunk-QZHKN3VN-DZleEj00.js} +2 -2
- package/dist/static/assets/{chunk-QZHKN3VN-BrrvAZdP.js.map → chunk-QZHKN3VN-DZleEj00.js.map} +1 -1
- package/dist/static/assets/{chunk-TZMSLE5B-DyKOlPTY.js → chunk-TZMSLE5B-CXxl_uqH.js} +2 -2
- package/dist/static/assets/{chunk-TZMSLE5B-DyKOlPTY.js.map → chunk-TZMSLE5B-CXxl_uqH.js.map} +1 -1
- package/dist/static/assets/{classDiagram-2ON5EDUG-FCrnlCWC.js → classDiagram-2ON5EDUG-C-7R0QB6.js} +6 -6
- package/dist/static/assets/{classDiagram-2ON5EDUG-FCrnlCWC.js.map → classDiagram-2ON5EDUG-C-7R0QB6.js.map} +1 -1
- package/dist/static/assets/{classDiagram-v2-WZHVMYZB-FCrnlCWC.js → classDiagram-v2-WZHVMYZB-C-7R0QB6.js} +6 -6
- package/dist/static/assets/{classDiagram-v2-WZHVMYZB-FCrnlCWC.js.map → classDiagram-v2-WZHVMYZB-C-7R0QB6.js.map} +1 -1
- package/dist/static/assets/{clone-BlI81KqZ.js → clone-BwOKYSj8.js} +2 -2
- package/dist/static/assets/{clone-BlI81KqZ.js.map → clone-BwOKYSj8.js.map} +1 -1
- package/dist/static/assets/{cose-bilkent-S5V4N54A-yM7S2atz.js → cose-bilkent-S5V4N54A-BCBalM7p.js} +2 -2
- package/dist/static/assets/{cose-bilkent-S5V4N54A-yM7S2atz.js.map → cose-bilkent-S5V4N54A-BCBalM7p.js.map} +1 -1
- package/dist/static/assets/{dagre-6UL2VRFP-BcweuZHt.js → dagre-6UL2VRFP-uV2ekQoj.js} +7 -7
- package/dist/static/assets/{dagre-6UL2VRFP-BcweuZHt.js.map → dagre-6UL2VRFP-uV2ekQoj.js.map} +1 -1
- package/dist/static/assets/{diagram-PSM6KHXK-D4-QwLW1.js → diagram-PSM6KHXK-D-ZMog1-.js} +8 -8
- package/dist/static/assets/{diagram-PSM6KHXK-D4-QwLW1.js.map → diagram-PSM6KHXK-D-ZMog1-.js.map} +1 -1
- package/dist/static/assets/{diagram-QEK2KX5R-BVbuejJn.js → diagram-QEK2KX5R-BThSELUH.js} +7 -7
- package/dist/static/assets/{diagram-QEK2KX5R-BVbuejJn.js.map → diagram-QEK2KX5R-BThSELUH.js.map} +1 -1
- package/dist/static/assets/{diagram-S2PKOQOG-pB6N6Tq_.js → diagram-S2PKOQOG-Di-YN5cd.js} +7 -7
- package/dist/static/assets/{diagram-S2PKOQOG-pB6N6Tq_.js.map → diagram-S2PKOQOG-Di-YN5cd.js.map} +1 -1
- package/dist/static/assets/{erDiagram-Q2GNP2WA-DLKmthuw.js → erDiagram-Q2GNP2WA-lBZ9DITn.js} +5 -5
- package/dist/static/assets/{erDiagram-Q2GNP2WA-DLKmthuw.js.map → erDiagram-Q2GNP2WA-lBZ9DITn.js.map} +1 -1
- package/dist/static/assets/{flowDiagram-NV44I4VS-BsBhWukh.js → flowDiagram-NV44I4VS-C_60PNQR.js} +6 -6
- package/dist/static/assets/{flowDiagram-NV44I4VS-BsBhWukh.js.map → flowDiagram-NV44I4VS-C_60PNQR.js.map} +1 -1
- package/dist/static/assets/{ganttDiagram-JELNMOA3-Debz-J-C.js → ganttDiagram-JELNMOA3-Dvqq-VHJ.js} +3 -3
- package/dist/static/assets/{ganttDiagram-JELNMOA3-Debz-J-C.js.map → ganttDiagram-JELNMOA3-Dvqq-VHJ.js.map} +1 -1
- package/dist/static/assets/{gitGraphDiagram-V2S2FVAM-BnAPFBGR.js → gitGraphDiagram-V2S2FVAM-BTj8orRe.js} +8 -8
- package/dist/static/assets/{gitGraphDiagram-V2S2FVAM-BnAPFBGR.js.map → gitGraphDiagram-V2S2FVAM-BTj8orRe.js.map} +1 -1
- package/dist/static/assets/{graph-DbzWiBNK.js → graph-BqCzR2Nl.js} +3 -3
- package/dist/static/assets/{graph-DbzWiBNK.js.map → graph-BqCzR2Nl.js.map} +1 -1
- package/dist/static/assets/{index-B-8J28g7.js → index-DrTqAfFy.js} +386 -201
- package/dist/static/assets/index-DrTqAfFy.js.map +1 -0
- package/dist/static/assets/{infoDiagram-HS3SLOUP-CZ5hWoxV.js → infoDiagram-HS3SLOUP-DlC6wsrv.js} +6 -6
- package/dist/static/assets/{infoDiagram-HS3SLOUP-CZ5hWoxV.js.map → infoDiagram-HS3SLOUP-DlC6wsrv.js.map} +1 -1
- package/dist/static/assets/{journeyDiagram-XKPGCS4Q-CKN3oSxk.js → journeyDiagram-XKPGCS4Q-Dg_RgtQX.js} +5 -5
- package/dist/static/assets/{journeyDiagram-XKPGCS4Q-CKN3oSxk.js.map → journeyDiagram-XKPGCS4Q-Dg_RgtQX.js.map} +1 -1
- package/dist/static/assets/{kanban-definition-3W4ZIXB7-BQCMklfJ.js → kanban-definition-3W4ZIXB7-DuGS3lId.js} +3 -3
- package/dist/static/assets/{kanban-definition-3W4ZIXB7-BQCMklfJ.js.map → kanban-definition-3W4ZIXB7-DuGS3lId.js.map} +1 -1
- package/dist/static/assets/{layout-C5B58szc.js → layout-FDz2bstZ.js} +5 -5
- package/dist/static/assets/{layout-C5B58szc.js.map → layout-FDz2bstZ.js.map} +1 -1
- package/dist/static/assets/{linear-_32fut6G.js → linear-CzsdvPGb.js} +2 -2
- package/dist/static/assets/{linear-_32fut6G.js.map → linear-CzsdvPGb.js.map} +1 -1
- package/dist/static/assets/{mindmap-definition-VGOIOE7T-C_goMzjx.js → mindmap-definition-VGOIOE7T-WsAF5UNp.js} +4 -4
- package/dist/static/assets/{mindmap-definition-VGOIOE7T-C_goMzjx.js.map → mindmap-definition-VGOIOE7T-WsAF5UNp.js.map} +1 -1
- package/dist/static/assets/{pieDiagram-ADFJNKIX-BQ2n0cOB.js → pieDiagram-ADFJNKIX-DJpRJ5ei.js} +8 -8
- package/dist/static/assets/{pieDiagram-ADFJNKIX-BQ2n0cOB.js.map → pieDiagram-ADFJNKIX-DJpRJ5ei.js.map} +1 -1
- package/dist/static/assets/{quadrantDiagram-AYHSOK5B-BLg7_neg.js → quadrantDiagram-AYHSOK5B-CMyIzTkY.js} +3 -3
- package/dist/static/assets/{quadrantDiagram-AYHSOK5B-BLg7_neg.js.map → quadrantDiagram-AYHSOK5B-CMyIzTkY.js.map} +1 -1
- package/dist/static/assets/{requirementDiagram-UZGBJVZJ-DwkJt0zi.js → requirementDiagram-UZGBJVZJ-D_yqVXGu.js} +4 -4
- package/dist/static/assets/{requirementDiagram-UZGBJVZJ-DwkJt0zi.js.map → requirementDiagram-UZGBJVZJ-D_yqVXGu.js.map} +1 -1
- package/dist/static/assets/{sankeyDiagram-TZEHDZUN-DmxmatUB.js → sankeyDiagram-TZEHDZUN-D4-cF724.js} +2 -2
- package/dist/static/assets/{sankeyDiagram-TZEHDZUN-DmxmatUB.js.map → sankeyDiagram-TZEHDZUN-D4-cF724.js.map} +1 -1
- package/dist/static/assets/{sequenceDiagram-WL72ISMW-KHU_eApU.js → sequenceDiagram-WL72ISMW-B7J3gWYN.js} +4 -4
- package/dist/static/assets/{sequenceDiagram-WL72ISMW-KHU_eApU.js.map → sequenceDiagram-WL72ISMW-B7J3gWYN.js.map} +1 -1
- package/dist/static/assets/{stateDiagram-FKZM4ZOC-B3DBCxAL.js → stateDiagram-FKZM4ZOC-DwEYYCcu.js} +9 -9
- package/dist/static/assets/{stateDiagram-FKZM4ZOC-B3DBCxAL.js.map → stateDiagram-FKZM4ZOC-DwEYYCcu.js.map} +1 -1
- package/dist/static/assets/{stateDiagram-v2-4FDKWEC3-C-uIk7gh.js → stateDiagram-v2-4FDKWEC3-D4LOOQV5.js} +5 -5
- package/dist/static/assets/{stateDiagram-v2-4FDKWEC3-C-uIk7gh.js.map → stateDiagram-v2-4FDKWEC3-D4LOOQV5.js.map} +1 -1
- package/dist/static/assets/{timeline-definition-IT6M3QCI-SysEcQCC.js → timeline-definition-IT6M3QCI-CyG-TJ_A.js} +3 -3
- package/dist/static/assets/{timeline-definition-IT6M3QCI-SysEcQCC.js.map → timeline-definition-IT6M3QCI-CyG-TJ_A.js.map} +1 -1
- package/dist/static/assets/{treemap-GDKQZRPO-d0AbKEc4.js → treemap-GDKQZRPO-yY4GiKmU.js} +5 -5
- package/dist/static/assets/{treemap-GDKQZRPO-d0AbKEc4.js.map → treemap-GDKQZRPO-yY4GiKmU.js.map} +1 -1
- package/dist/static/assets/{xychartDiagram-PRI3JC2R-CmSQMxUh.js → xychartDiagram-PRI3JC2R-5TYN_q15.js} +3 -3
- package/dist/static/assets/{xychartDiagram-PRI3JC2R-CmSQMxUh.js.map → xychartDiagram-PRI3JC2R-5TYN_q15.js.map} +1 -1
- package/dist/static/index.html +1 -1
- package/dist/team.js +33 -4
- package/dist/tools/app-reminders.js +280 -0
- package/dist/tools/prompts/memory/en/errors.md +155 -0
- package/dist/tools/prompts/memory/en/index.md +47 -0
- package/dist/tools/prompts/memory/en/principles.md +79 -0
- package/dist/tools/prompts/memory/en/scenarios.md +174 -0
- package/dist/tools/prompts/memory/en/tools.md +154 -0
- package/dist/tools/prompts/memory/zh/errors.md +155 -0
- package/dist/tools/prompts/memory/zh/index.md +47 -0
- package/dist/tools/prompts/memory/zh/principles.md +79 -0
- package/dist/tools/prompts/memory/zh/scenarios.md +174 -0
- package/dist/tools/prompts/memory/zh/tools.md +154 -0
- package/dist/tools/ripgrep.js +197 -63
- package/package.json +2 -2
- package/dist/static/assets/index-B-8J28g7.js.map +0 -1
package/README.md
CHANGED
|
@@ -107,6 +107,30 @@ Notes:
|
|
|
107
107
|
- `npx dominds` does **not** necessarily mean “always latest”: if your project already has `dominds` installed, or your npm cache already contains an older version, `npx` may reuse that.
|
|
108
108
|
- If you want “resolve whatever `latest` points to”, use `dominds@latest` explicitly. It may still reuse the cached copy of that same version (expected).
|
|
109
109
|
|
|
110
|
+
### Installing Dominds apps
|
|
111
|
+
|
|
112
|
+
Installing the `dominds` CLI itself and installing a **Dominds app** into your current rtws are two different operations:
|
|
113
|
+
|
|
114
|
+
- `npm install -g dominds` / `pnpm add -g dominds`: install the Dominds CLI.
|
|
115
|
+
- `npx -y dominds@latest`: run the Dominds CLI without a global install.
|
|
116
|
+
- `dominds install <app>`: add a Dominds app into the current rtws capability graph.
|
|
117
|
+
|
|
118
|
+
Examples:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
# Install a published app into the current rtws
|
|
122
|
+
dominds install @longrun-ai/web-dev --enable
|
|
123
|
+
|
|
124
|
+
# Install a local app under development into the current rtws
|
|
125
|
+
dominds install ./dominds-apps/web-dev --local --enable
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Important distinctions:
|
|
129
|
+
|
|
130
|
+
- `npm install` / `pnpm add` only download packages; they do **not** register an app into `.minds/app.yaml` for the current rtws.
|
|
131
|
+
- `npx` is used by the kernel/CLI as a package-execution backend. For app installation, the kernel may execute `npx -y <pkg> --dominds-app` under the hood.
|
|
132
|
+
- `--dominds-app` is a low-level app handshake flag between the kernel/CLI and the app package. It is not the normal end-user interface.
|
|
133
|
+
|
|
110
134
|
For development and any sort of open source contribution, use the in-tree dev wrapper rtws:
|
|
111
135
|
|
|
112
136
|
https://github.com/longrun-ai/dominds-feat-dev
|
package/README.zh.md
CHANGED
|
@@ -62,6 +62,30 @@ npx -y dominds@latest
|
|
|
62
62
|
- 仅写 `npx dominds` **不一定**会自动跑“最新版”:如果你本地项目已安装了 `dominds`,或 npm cache 已存在旧版本,`npx` 可能会直接复用已有版本。
|
|
63
63
|
- 想要“每次都按 latest 标签解析版本”,请显式写 `dominds@latest`;它仍可能复用 cache 中已下载的同版本(属于正常行为)。
|
|
64
64
|
|
|
65
|
+
### 安装 Dominds app
|
|
66
|
+
|
|
67
|
+
安装 `dominds` CLI 本体,和把一个 **Dominds app** 装进当前 rtws,是两件不同的事:
|
|
68
|
+
|
|
69
|
+
- `npm install -g dominds` / `pnpm add -g dominds`:安装 Dominds CLI。
|
|
70
|
+
- `npx -y dominds@latest`:无需全局安装,直接运行 Dominds CLI。
|
|
71
|
+
- `dominds install <app>`:把一个 Dominds app 纳入当前 rtws 的能力图。
|
|
72
|
+
|
|
73
|
+
示例:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
# 安装已发布 app 到当前 rtws
|
|
77
|
+
dominds install @longrun-ai/web-dev --enable
|
|
78
|
+
|
|
79
|
+
# 安装本地开发中的 app 到当前 rtws
|
|
80
|
+
dominds install ./dominds-apps/web-dev --local --enable
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
几个关键区别:
|
|
84
|
+
|
|
85
|
+
- `npm install` / `pnpm add` 只负责下载包;它们**不会**自动把 app 登记到当前 rtws 的 `.minds/app.yaml`。
|
|
86
|
+
- `npx` 在这里主要是 Kernel/CLI 的包执行后端。安装 app 时,Kernel 可能会在底层执行 `npx -y <pkg> --dominds-app` 来做握手。
|
|
87
|
+
- `--dominds-app` 是 Kernel/CLI 与 app 包之间的底层握手参数,不是普通用户的常规操作入口。
|
|
88
|
+
|
|
65
89
|
### 开发与贡献(树内工作流 / in-tree 工作流)
|
|
66
90
|
|
|
67
91
|
若你计划为 Dominds 开源项目贡献代码,建议使用树内包装器(in-tree wrapper)运行时工作区(rtws):
|
package/dist/apps/app-json.js
CHANGED
|
@@ -90,9 +90,27 @@ function parseDialogRunControlJson(v, at) {
|
|
|
90
90
|
},
|
|
91
91
|
};
|
|
92
92
|
}
|
|
93
|
-
function
|
|
93
|
+
function parseReminderOwnerJson(v, at) {
|
|
94
94
|
if (!isRecord(v))
|
|
95
|
-
return { ok: false, errorText:
|
|
95
|
+
return { ok: false, errorText: `Invalid ${at}: expected object` };
|
|
96
|
+
const ref = asString(v['ref']);
|
|
97
|
+
if (!ref || ref.trim() === '')
|
|
98
|
+
return { ok: false, errorText: `Invalid ${at}.ref: required` };
|
|
99
|
+
const managedByTool = asString(v['managedByTool']) ?? undefined;
|
|
100
|
+
const updateExample = asString(v['updateExample']) ?? undefined;
|
|
101
|
+
return {
|
|
102
|
+
ok: true,
|
|
103
|
+
owner: {
|
|
104
|
+
ref,
|
|
105
|
+
managedByTool,
|
|
106
|
+
updateExample,
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function parseDomindsAppInstallJson(v) {
|
|
111
|
+
if (!isRecord(v)) {
|
|
112
|
+
return { ok: false, errorText: 'Invalid app --dominds-app output: expected object' };
|
|
113
|
+
}
|
|
96
114
|
const schemaVersion = v['schemaVersion'];
|
|
97
115
|
if (schemaVersion !== 1) {
|
|
98
116
|
return { ok: false, errorText: `Unsupported app json schemaVersion: ${String(schemaVersion)}` };
|
|
@@ -189,7 +207,24 @@ function parseDomindsAppInstallJson(v) {
|
|
|
189
207
|
dialogRunControls.push(parsed.control);
|
|
190
208
|
}
|
|
191
209
|
}
|
|
192
|
-
|
|
210
|
+
const reminderOwnersRaw = contributesRaw['reminderOwners'];
|
|
211
|
+
let reminderOwners;
|
|
212
|
+
if (reminderOwnersRaw !== undefined) {
|
|
213
|
+
if (!Array.isArray(reminderOwnersRaw)) {
|
|
214
|
+
return {
|
|
215
|
+
ok: false,
|
|
216
|
+
errorText: 'Invalid app json: contributes.reminderOwners must be an array',
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
reminderOwners = [];
|
|
220
|
+
for (let i = 0; i < reminderOwnersRaw.length; i += 1) {
|
|
221
|
+
const parsed = parseReminderOwnerJson(reminderOwnersRaw[i], `contributes.reminderOwners[${i}]`);
|
|
222
|
+
if (!parsed.ok)
|
|
223
|
+
return parsed;
|
|
224
|
+
reminderOwners.push(parsed.owner);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
contributes = { teammatesYamlRelPath, toolsets, dialogRunControls, reminderOwners };
|
|
193
228
|
}
|
|
194
229
|
return {
|
|
195
230
|
ok: true,
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.clearAppDialogRunControlsRegistry = clearAppDialogRunControlsRegistry;
|
|
4
4
|
exports.registerAppDialogRunControl = registerAppDialogRunControl;
|
|
5
|
+
exports.unregisterAppDialogRunControl = unregisterAppDialogRunControl;
|
|
5
6
|
exports.getAppDialogRunControlMeta = getAppDialogRunControlMeta;
|
|
6
7
|
exports.listAppDialogRunControls = listAppDialogRunControls;
|
|
7
8
|
const runControlRegistry = new Map();
|
|
@@ -22,6 +23,9 @@ function registerAppDialogRunControl(params) {
|
|
|
22
23
|
descriptionI18n: params.descriptionI18n,
|
|
23
24
|
});
|
|
24
25
|
}
|
|
26
|
+
function unregisterAppDialogRunControl(id) {
|
|
27
|
+
runControlRegistry.delete(id);
|
|
28
|
+
}
|
|
25
29
|
function getAppDialogRunControlMeta(id) {
|
|
26
30
|
const found = runControlRegistry.get(id);
|
|
27
31
|
return found ?? null;
|
|
@@ -62,9 +62,15 @@ function getResolutionHint(params) {
|
|
|
62
62
|
`Default local root is 'dominds-apps' (rtws-relative) and expects local apps at '<root>/<appId>/'.`);
|
|
63
63
|
}
|
|
64
64
|
async function resolveLocalAppPackageRootAbs(params) {
|
|
65
|
+
const candidates = new Set();
|
|
66
|
+
if (params.previousResolutionEntry?.source.kind === 'local') {
|
|
67
|
+
candidates.add(params.previousResolutionEntry.source.pathAbs);
|
|
68
|
+
}
|
|
65
69
|
for (const root of params.localRoots) {
|
|
66
70
|
const rootAbs = node_path_1.default.isAbsolute(root) ? root : node_path_1.default.resolve(params.rtwsRootAbs, root);
|
|
67
|
-
|
|
71
|
+
candidates.add(node_path_1.default.resolve(rootAbs, params.appId));
|
|
72
|
+
}
|
|
73
|
+
for (const candidateAbs of candidates) {
|
|
68
74
|
if (await dirExists(candidateAbs))
|
|
69
75
|
return candidateAbs;
|
|
70
76
|
}
|
|
@@ -101,6 +107,7 @@ async function probeAppByStrategy(params) {
|
|
|
101
107
|
rtwsRootAbs: params.rtwsRootAbs,
|
|
102
108
|
appId: params.appId,
|
|
103
109
|
localRoots: params.strategy.localRoots,
|
|
110
|
+
previousResolutionEntry: params.previousResolutionEntry,
|
|
104
111
|
});
|
|
105
112
|
if (!packageRootAbs)
|
|
106
113
|
continue;
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.INSTALLED_APPS_REL_PATH = void 0;
|
|
7
|
+
exports.loadInstalledAppsFile = loadInstalledAppsFile;
|
|
8
|
+
exports.writeInstalledAppsFile = writeInstalledAppsFile;
|
|
9
|
+
exports.upsertInstalledApp = upsertInstalledApp;
|
|
10
|
+
exports.removeInstalledApp = removeInstalledApp;
|
|
11
|
+
exports.setAppEnabled = setAppEnabled;
|
|
12
|
+
exports.setAppRuntimePort = setAppRuntimePort;
|
|
13
|
+
exports.findInstalledApp = findInstalledApp;
|
|
14
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
15
|
+
const path_1 = __importDefault(require("path"));
|
|
16
|
+
const yaml_1 = __importDefault(require("yaml"));
|
|
17
|
+
const time_1 = require("../shared/utils/time");
|
|
18
|
+
exports.INSTALLED_APPS_REL_PATH = path_1.default.join('.apps', 'installed.yaml');
|
|
19
|
+
function isRecord(v) {
|
|
20
|
+
return typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
21
|
+
}
|
|
22
|
+
function asString(v) {
|
|
23
|
+
return typeof v === 'string' ? v : null;
|
|
24
|
+
}
|
|
25
|
+
function asBool(v) {
|
|
26
|
+
return typeof v === 'boolean' ? v : null;
|
|
27
|
+
}
|
|
28
|
+
function asNullableNumber(v) {
|
|
29
|
+
if (v === null)
|
|
30
|
+
return null;
|
|
31
|
+
if (typeof v === 'number' && Number.isFinite(v))
|
|
32
|
+
return v;
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
function parseSource(v, at) {
|
|
36
|
+
if (!isRecord(v))
|
|
37
|
+
return { ok: false, errorText: `Invalid ${at}: expected object` };
|
|
38
|
+
const kind = asString(v['kind']);
|
|
39
|
+
if (kind !== 'npx' && kind !== 'local') {
|
|
40
|
+
return { ok: false, errorText: `Invalid ${at}.kind: expected 'npx'|'local'` };
|
|
41
|
+
}
|
|
42
|
+
if (kind === 'npx') {
|
|
43
|
+
const spec = asString(v['spec']);
|
|
44
|
+
if (!spec || spec.trim() === '')
|
|
45
|
+
return { ok: false, errorText: `Invalid ${at}.spec: required` };
|
|
46
|
+
return { ok: true, source: { kind, spec } };
|
|
47
|
+
}
|
|
48
|
+
const pathAbs = asString(v['pathAbs']);
|
|
49
|
+
if (!pathAbs || pathAbs.trim() === '')
|
|
50
|
+
return { ok: false, errorText: `Invalid ${at}.pathAbs: required` };
|
|
51
|
+
return { ok: true, source: { kind, pathAbs } };
|
|
52
|
+
}
|
|
53
|
+
function parseRuntime(v, at) {
|
|
54
|
+
if (!isRecord(v))
|
|
55
|
+
return { ok: false, errorText: `Invalid ${at}: expected object` };
|
|
56
|
+
const portRaw = v['port'];
|
|
57
|
+
const port = asNullableNumber(portRaw);
|
|
58
|
+
if (port === null) {
|
|
59
|
+
if (portRaw !== null) {
|
|
60
|
+
return { ok: false, errorText: `Invalid ${at}.port: expected number|null` };
|
|
61
|
+
}
|
|
62
|
+
return { ok: true, runtime: { port: null } };
|
|
63
|
+
}
|
|
64
|
+
if (port < 0 || !Number.isInteger(port))
|
|
65
|
+
return { ok: false, errorText: `Invalid ${at}.port: expected non-negative integer|null` };
|
|
66
|
+
return { ok: true, runtime: { port: port } };
|
|
67
|
+
}
|
|
68
|
+
function parseEntry(v, at) {
|
|
69
|
+
if (!isRecord(v))
|
|
70
|
+
return { ok: false, errorText: `Invalid ${at}: expected object` };
|
|
71
|
+
const id = asString(v['id']);
|
|
72
|
+
if (!id || id.trim() === '')
|
|
73
|
+
return { ok: false, errorText: `Invalid ${at}.id: required` };
|
|
74
|
+
const enabled = asBool(v['enabled']);
|
|
75
|
+
if (enabled === null)
|
|
76
|
+
return { ok: false, errorText: `Invalid ${at}.enabled: boolean required` };
|
|
77
|
+
const installedAt = asString(v['installedAt']);
|
|
78
|
+
const updatedAt = asString(v['updatedAt']);
|
|
79
|
+
if (!installedAt || installedAt.trim() === '')
|
|
80
|
+
return { ok: false, errorText: `Invalid ${at}.installedAt: required` };
|
|
81
|
+
if (!updatedAt || updatedAt.trim() === '')
|
|
82
|
+
return { ok: false, errorText: `Invalid ${at}.updatedAt: required` };
|
|
83
|
+
const sourceParsed = parseSource(v['source'], `${at}.source`);
|
|
84
|
+
if (!sourceParsed.ok)
|
|
85
|
+
return sourceParsed;
|
|
86
|
+
const runtimeParsed = parseRuntime(v['runtime'] ?? { port: null }, `${at}.runtime`);
|
|
87
|
+
if (!runtimeParsed.ok)
|
|
88
|
+
return runtimeParsed;
|
|
89
|
+
// installJson is validated by install/update command on write. On read we keep permissive:
|
|
90
|
+
// kernel will fail fast later if it needs a missing field.
|
|
91
|
+
const installJson = v['installJson'];
|
|
92
|
+
if (!isRecord(installJson))
|
|
93
|
+
return { ok: false, errorText: `Invalid ${at}.installJson: expected object` };
|
|
94
|
+
return {
|
|
95
|
+
ok: true,
|
|
96
|
+
entry: {
|
|
97
|
+
id,
|
|
98
|
+
enabled,
|
|
99
|
+
source: sourceParsed.source,
|
|
100
|
+
runtime: runtimeParsed.runtime,
|
|
101
|
+
installJson: installJson,
|
|
102
|
+
installedAt,
|
|
103
|
+
updatedAt,
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
async function loadInstalledAppsFile(params) {
|
|
108
|
+
const filePathAbs = path_1.default.resolve(params.rtwsRootAbs, exports.INSTALLED_APPS_REL_PATH);
|
|
109
|
+
let raw;
|
|
110
|
+
try {
|
|
111
|
+
raw = await promises_1.default.readFile(filePathAbs, 'utf-8');
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
const isEnoent = typeof err === 'object' &&
|
|
115
|
+
err !== null &&
|
|
116
|
+
'code' in err &&
|
|
117
|
+
err.code === 'ENOENT';
|
|
118
|
+
if (isEnoent) {
|
|
119
|
+
return { kind: 'ok', filePathAbs, file: { schemaVersion: 1, apps: [] } };
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
kind: 'error',
|
|
123
|
+
filePathAbs,
|
|
124
|
+
errorText: err instanceof Error ? err.message : String(err),
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
let parsed;
|
|
128
|
+
try {
|
|
129
|
+
parsed = yaml_1.default.parse(raw);
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
return {
|
|
133
|
+
kind: 'error',
|
|
134
|
+
filePathAbs,
|
|
135
|
+
errorText: `Failed to parse YAML: ${err instanceof Error ? err.message : String(err)}`,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
if (!isRecord(parsed)) {
|
|
139
|
+
return { kind: 'error', filePathAbs, errorText: 'Invalid installed.yaml: expected object' };
|
|
140
|
+
}
|
|
141
|
+
const schemaVersion = parsed['schemaVersion'];
|
|
142
|
+
if (schemaVersion !== 1) {
|
|
143
|
+
return {
|
|
144
|
+
kind: 'error',
|
|
145
|
+
filePathAbs,
|
|
146
|
+
errorText: `Unsupported schemaVersion: ${String(schemaVersion)}`,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
const appsRaw = parsed['apps'];
|
|
150
|
+
if (!Array.isArray(appsRaw)) {
|
|
151
|
+
return {
|
|
152
|
+
kind: 'error',
|
|
153
|
+
filePathAbs,
|
|
154
|
+
errorText: 'Invalid installed.yaml: apps must be an array',
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
const apps = [];
|
|
158
|
+
for (let i = 0; i < appsRaw.length; i += 1) {
|
|
159
|
+
const e = parseEntry(appsRaw[i], `apps[${i}]`);
|
|
160
|
+
if (!e.ok)
|
|
161
|
+
return { kind: 'error', filePathAbs, errorText: e.errorText };
|
|
162
|
+
apps.push(e.entry);
|
|
163
|
+
}
|
|
164
|
+
return { kind: 'ok', filePathAbs, file: { schemaVersion: 1, apps } };
|
|
165
|
+
}
|
|
166
|
+
async function writeInstalledAppsFile(params) {
|
|
167
|
+
const dirAbs = path_1.default.resolve(params.rtwsRootAbs, '.apps');
|
|
168
|
+
await promises_1.default.mkdir(dirAbs, { recursive: true });
|
|
169
|
+
const filePathAbs = path_1.default.resolve(params.rtwsRootAbs, exports.INSTALLED_APPS_REL_PATH);
|
|
170
|
+
const yamlText = yaml_1.default.stringify(params.file);
|
|
171
|
+
await promises_1.default.writeFile(filePathAbs, yamlText, 'utf-8');
|
|
172
|
+
}
|
|
173
|
+
function upsertInstalledApp(params) {
|
|
174
|
+
const apps = [...params.existing.apps];
|
|
175
|
+
const idx = apps.findIndex((a) => a.id === params.next.id);
|
|
176
|
+
if (idx >= 0) {
|
|
177
|
+
apps[idx] = params.next;
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
apps.push(params.next);
|
|
181
|
+
}
|
|
182
|
+
return { schemaVersion: 1, apps };
|
|
183
|
+
}
|
|
184
|
+
function removeInstalledApp(params) {
|
|
185
|
+
return { schemaVersion: 1, apps: params.existing.apps.filter((a) => a.id !== params.appId) };
|
|
186
|
+
}
|
|
187
|
+
function setAppEnabled(params) {
|
|
188
|
+
const now = (0, time_1.formatUnifiedTimestamp)(new Date());
|
|
189
|
+
const apps = params.existing.apps.map((a) => a.id === params.appId ? { ...a, enabled: params.enabled, updatedAt: now } : a);
|
|
190
|
+
return { schemaVersion: 1, apps };
|
|
191
|
+
}
|
|
192
|
+
function setAppRuntimePort(params) {
|
|
193
|
+
const existingApp = findInstalledApp(params.existing, params.appId);
|
|
194
|
+
if (!existingApp)
|
|
195
|
+
return params.existing;
|
|
196
|
+
if (existingApp.runtime.port === params.port)
|
|
197
|
+
return params.existing;
|
|
198
|
+
const now = (0, time_1.formatUnifiedTimestamp)(new Date());
|
|
199
|
+
const apps = params.existing.apps.map((a) => a.id === params.appId
|
|
200
|
+
? { ...a, runtime: { ...a.runtime, port: params.port }, updatedAt: now }
|
|
201
|
+
: a);
|
|
202
|
+
return { schemaVersion: 1, apps };
|
|
203
|
+
}
|
|
204
|
+
function findInstalledApp(file, appId) {
|
|
205
|
+
const found = file.apps.find((a) => a.id === appId);
|
|
206
|
+
return found ?? null;
|
|
207
|
+
}
|
|
@@ -61,26 +61,26 @@ function parseAppJsonFromStdout(stdout, where) {
|
|
|
61
61
|
return res.json;
|
|
62
62
|
}
|
|
63
63
|
async function runDomindsAppJsonViaNpx(params) {
|
|
64
|
-
const { stdout, stderr } = await execFileAsync('npx', ['-y', params.spec, '--
|
|
64
|
+
const { stdout, stderr } = await execFileAsync('npx', ['-y', params.spec, '--dominds-app'], {
|
|
65
65
|
cwd: params.cwdAbs,
|
|
66
66
|
env: process.env,
|
|
67
67
|
maxBuffer: 10 * 1024 * 1024,
|
|
68
68
|
});
|
|
69
69
|
if (stderr.trim() !== '') {
|
|
70
70
|
// Loud: stderr indicates non-JSON noise that would break parsing determinism.
|
|
71
|
-
throw new Error(`App printed to stderr during --
|
|
71
|
+
throw new Error(`App printed to stderr during --dominds-app handshake (spec=${params.spec}):\n${stderr}`);
|
|
72
72
|
}
|
|
73
|
-
return parseAppJsonFromStdout(stdout, `npx ${params.spec} --
|
|
73
|
+
return parseAppJsonFromStdout(stdout, `npx ${params.spec} --dominds-app`);
|
|
74
74
|
}
|
|
75
75
|
async function runDomindsAppJsonViaLocalPackage(params) {
|
|
76
76
|
const scriptAbs = await resolveLocalBinScriptAbs(params.packageRootAbs);
|
|
77
|
-
const { stdout, stderr } = await execFileAsync(process.execPath, [scriptAbs, '--
|
|
77
|
+
const { stdout, stderr } = await execFileAsync(process.execPath, [scriptAbs, '--dominds-app'], {
|
|
78
78
|
cwd: params.packageRootAbs,
|
|
79
79
|
env: process.env,
|
|
80
80
|
maxBuffer: 10 * 1024 * 1024,
|
|
81
81
|
});
|
|
82
82
|
if (stderr.trim() !== '') {
|
|
83
|
-
throw new Error(`Local app printed to stderr during --
|
|
83
|
+
throw new Error(`Local app printed to stderr during --dominds-app handshake (path=${params.packageRootAbs}):\n${stderr}`);
|
|
84
84
|
}
|
|
85
|
-
return parseAppJsonFromStdout(stdout, `local ${params.packageRootAbs} --
|
|
85
|
+
return parseAppJsonFromStdout(stdout, `local ${params.packageRootAbs} --dominds-app`);
|
|
86
86
|
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.resolveStableAppRuntimePort = resolveStableAppRuntimePort;
|
|
7
|
+
const node_net_1 = __importDefault(require("node:net"));
|
|
8
|
+
const STABLE_PORT_RANGE_START = 43000;
|
|
9
|
+
const STABLE_PORT_RANGE_END = 49999;
|
|
10
|
+
const PORT_MAX = 65535;
|
|
11
|
+
function isPositivePort(value) {
|
|
12
|
+
return (typeof value === 'number' &&
|
|
13
|
+
Number.isInteger(value) &&
|
|
14
|
+
value > 0 &&
|
|
15
|
+
value <= PORT_MAX &&
|
|
16
|
+
Number.isFinite(value));
|
|
17
|
+
}
|
|
18
|
+
function hashAppId(appId) {
|
|
19
|
+
// FNV-1a 32-bit hash for deterministic port probing order.
|
|
20
|
+
let hash = 0x811c9dc5;
|
|
21
|
+
for (let i = 0; i < appId.length; i += 1) {
|
|
22
|
+
hash ^= appId.charCodeAt(i);
|
|
23
|
+
hash = Math.imul(hash, 0x01000193);
|
|
24
|
+
}
|
|
25
|
+
return hash >>> 0;
|
|
26
|
+
}
|
|
27
|
+
function collectReservedPorts(existingApps, appId) {
|
|
28
|
+
const reserved = new Set();
|
|
29
|
+
for (const app of existingApps) {
|
|
30
|
+
if (app.id === appId)
|
|
31
|
+
continue;
|
|
32
|
+
if (isPositivePort(app.runtime.port)) {
|
|
33
|
+
reserved.add(app.runtime.port);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return reserved;
|
|
37
|
+
}
|
|
38
|
+
async function canBindPort(port) {
|
|
39
|
+
return await new Promise((resolve) => {
|
|
40
|
+
const server = node_net_1.default.createServer();
|
|
41
|
+
server.unref();
|
|
42
|
+
const finish = (ok) => {
|
|
43
|
+
server.removeAllListeners('error');
|
|
44
|
+
server.removeAllListeners('listening');
|
|
45
|
+
resolve(ok);
|
|
46
|
+
};
|
|
47
|
+
server.once('error', () => {
|
|
48
|
+
finish(false);
|
|
49
|
+
});
|
|
50
|
+
server.once('listening', () => {
|
|
51
|
+
server.close(() => finish(true));
|
|
52
|
+
});
|
|
53
|
+
server.listen({ host: '127.0.0.1', port, exclusive: true });
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
async function pickDeterministicAvailablePort(params) {
|
|
57
|
+
const rangeSize = STABLE_PORT_RANGE_END - STABLE_PORT_RANGE_START + 1;
|
|
58
|
+
if (rangeSize <= 0) {
|
|
59
|
+
throw new Error('Invalid stable app runtime port range configuration');
|
|
60
|
+
}
|
|
61
|
+
const baseHash = hashAppId(params.appId);
|
|
62
|
+
for (let i = 0; i < rangeSize; i += 1) {
|
|
63
|
+
const candidate = STABLE_PORT_RANGE_START + ((baseHash + i) % rangeSize);
|
|
64
|
+
if (params.reservedPorts.has(candidate))
|
|
65
|
+
continue;
|
|
66
|
+
if (await canBindPort(candidate))
|
|
67
|
+
return candidate;
|
|
68
|
+
}
|
|
69
|
+
throw new Error(`Failed to allocate stable runtime port for app '${params.appId}': no bindable port in ${STABLE_PORT_RANGE_START}-${STABLE_PORT_RANGE_END}`);
|
|
70
|
+
}
|
|
71
|
+
async function resolveStableAppRuntimePort(params) {
|
|
72
|
+
if (!params.installJson.frontend)
|
|
73
|
+
return null;
|
|
74
|
+
const reservedPorts = collectReservedPorts(params.existingApps, params.appId);
|
|
75
|
+
if (isPositivePort(params.existingRuntimePort)) {
|
|
76
|
+
if (reservedPorts.has(params.existingRuntimePort)) {
|
|
77
|
+
throw new Error(`Invalid installed apps state: runtime port ${params.existingRuntimePort} for '${params.appId}' collides with another installed app`);
|
|
78
|
+
}
|
|
79
|
+
return params.existingRuntimePort;
|
|
80
|
+
}
|
|
81
|
+
const defaultPort = params.installJson.frontend.defaultPort;
|
|
82
|
+
if (isPositivePort(defaultPort) &&
|
|
83
|
+
!reservedPorts.has(defaultPort) &&
|
|
84
|
+
(await canBindPort(defaultPort))) {
|
|
85
|
+
return defaultPort;
|
|
86
|
+
}
|
|
87
|
+
return await pickDeterministicAvailablePort({
|
|
88
|
+
appId: params.appId,
|
|
89
|
+
reservedPorts,
|
|
90
|
+
});
|
|
91
|
+
}
|