dominds 1.3.0 → 1.4.1
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 +9 -2
- package/README.zh.md +9 -2
- package/dist/apps/app-lock-file.js +33 -57
- package/dist/apps/configuration-file.js +267 -0
- package/dist/apps/enabled-apps.js +217 -252
- package/dist/apps/manifest.js +132 -0
- package/dist/apps/resolution-file.js +80 -192
- package/dist/apps/workspace-app-state.js +8 -0
- package/dist/cli/disable.js +18 -22
- package/dist/cli/enable.js +20 -58
- package/dist/cli/install.js +74 -80
- package/dist/cli/uninstall.js +48 -25
- package/dist/cli/update.js +36 -80
- package/dist/docs/app-constitution.md +12 -7
- package/dist/docs/app-constitution.zh.md +13 -8
- package/dist/docs/context-health.md +17 -5
- package/dist/minds/system-prompt-parts.js +4 -4
- package/dist/server/api-routes.js +577 -80
- package/dist/server/server-core.js +19 -4
- package/dist/shared/i18n/driver-messages.js +14 -12
- package/dist/static/assets/{_basePickBy-BMCtwrV7.js → _basePickBy-B2o4z1Hf.js} +3 -3
- package/dist/static/assets/{_basePickBy-BMCtwrV7.js.map → _basePickBy-B2o4z1Hf.js.map} +1 -1
- package/dist/static/assets/{_baseUniq-BuyCgJiA.js → _baseUniq-CLmcxjdl.js} +2 -2
- package/dist/static/assets/{_baseUniq-BuyCgJiA.js.map → _baseUniq-CLmcxjdl.js.map} +1 -1
- package/dist/static/assets/{arc-BDuN8lwA.js → arc-CymD_KN7.js} +2 -2
- package/dist/static/assets/{arc-BDuN8lwA.js.map → arc-CymD_KN7.js.map} +1 -1
- package/dist/static/assets/{architectureDiagram-VXUJARFQ-C-ekqGAD.js → architectureDiagram-VXUJARFQ-DJQfSJUH.js} +7 -7
- package/dist/static/assets/{architectureDiagram-VXUJARFQ-C-ekqGAD.js.map → architectureDiagram-VXUJARFQ-DJQfSJUH.js.map} +1 -1
- package/dist/static/assets/{blockDiagram-VD42YOAC-CgQiNuuQ.js → blockDiagram-VD42YOAC-pHVz60D0.js} +7 -7
- package/dist/static/assets/{blockDiagram-VD42YOAC-CgQiNuuQ.js.map → blockDiagram-VD42YOAC-pHVz60D0.js.map} +1 -1
- package/dist/static/assets/{c4Diagram-YG6GDRKO-DONC39q-.js → c4Diagram-YG6GDRKO-B0WnCfAT.js} +3 -3
- package/dist/static/assets/{c4Diagram-YG6GDRKO-DONC39q-.js.map → c4Diagram-YG6GDRKO-B0WnCfAT.js.map} +1 -1
- package/dist/static/assets/{channel-CJTFwXIG.js → channel-CX9BlKil.js} +2 -2
- package/dist/static/assets/{channel-CJTFwXIG.js.map → channel-CX9BlKil.js.map} +1 -1
- package/dist/static/assets/{chunk-4BX2VUAB-NaIy4uLJ.js → chunk-4BX2VUAB-lXArRj3o.js} +2 -2
- package/dist/static/assets/{chunk-4BX2VUAB-NaIy4uLJ.js.map → chunk-4BX2VUAB-lXArRj3o.js.map} +1 -1
- package/dist/static/assets/{chunk-55IACEB6-JUKI_Ayx.js → chunk-55IACEB6-CdqwynH9.js} +2 -2
- package/dist/static/assets/{chunk-55IACEB6-JUKI_Ayx.js.map → chunk-55IACEB6-CdqwynH9.js.map} +1 -1
- package/dist/static/assets/{chunk-B4BG7PRW-dIswFJDn.js → chunk-B4BG7PRW-Y-uXcJst.js} +5 -5
- package/dist/static/assets/{chunk-B4BG7PRW-dIswFJDn.js.map → chunk-B4BG7PRW-Y-uXcJst.js.map} +1 -1
- package/dist/static/assets/{chunk-DI55MBZ5-DU2b_N30.js → chunk-DI55MBZ5-C5xSbRST.js} +4 -4
- package/dist/static/assets/{chunk-DI55MBZ5-DU2b_N30.js.map → chunk-DI55MBZ5-C5xSbRST.js.map} +1 -1
- package/dist/static/assets/{chunk-FMBD7UC4-BgExcScw.js → chunk-FMBD7UC4-5uefwCjI.js} +2 -2
- package/dist/static/assets/{chunk-FMBD7UC4-BgExcScw.js.map → chunk-FMBD7UC4-5uefwCjI.js.map} +1 -1
- package/dist/static/assets/{chunk-QN33PNHL-bitxyqh7.js → chunk-QN33PNHL-DzWVcvpI.js} +2 -2
- package/dist/static/assets/{chunk-QN33PNHL-bitxyqh7.js.map → chunk-QN33PNHL-DzWVcvpI.js.map} +1 -1
- package/dist/static/assets/{chunk-QZHKN3VN-Cor8u7DT.js → chunk-QZHKN3VN-BrrvAZdP.js} +2 -2
- package/dist/static/assets/{chunk-QZHKN3VN-Cor8u7DT.js.map → chunk-QZHKN3VN-BrrvAZdP.js.map} +1 -1
- package/dist/static/assets/{chunk-TZMSLE5B-Aceoxav_.js → chunk-TZMSLE5B-DyKOlPTY.js} +2 -2
- package/dist/static/assets/{chunk-TZMSLE5B-Aceoxav_.js.map → chunk-TZMSLE5B-DyKOlPTY.js.map} +1 -1
- package/dist/static/assets/{classDiagram-2ON5EDUG-D1Q6a8Hg.js → classDiagram-2ON5EDUG-FCrnlCWC.js} +6 -6
- package/dist/static/assets/{classDiagram-2ON5EDUG-D1Q6a8Hg.js.map → classDiagram-2ON5EDUG-FCrnlCWC.js.map} +1 -1
- package/dist/static/assets/{classDiagram-v2-WZHVMYZB-D1Q6a8Hg.js → classDiagram-v2-WZHVMYZB-FCrnlCWC.js} +6 -6
- package/dist/static/assets/{classDiagram-v2-WZHVMYZB-D1Q6a8Hg.js.map → classDiagram-v2-WZHVMYZB-FCrnlCWC.js.map} +1 -1
- package/dist/static/assets/{clone-MlWbv1V0.js → clone-BlI81KqZ.js} +2 -2
- package/dist/static/assets/{clone-MlWbv1V0.js.map → clone-BlI81KqZ.js.map} +1 -1
- package/dist/static/assets/{cose-bilkent-S5V4N54A-DWPCXSrn.js → cose-bilkent-S5V4N54A-yM7S2atz.js} +2 -2
- package/dist/static/assets/{cose-bilkent-S5V4N54A-DWPCXSrn.js.map → cose-bilkent-S5V4N54A-yM7S2atz.js.map} +1 -1
- package/dist/static/assets/{dagre-6UL2VRFP-C8ptQ9V3.js → dagre-6UL2VRFP-BcweuZHt.js} +7 -7
- package/dist/static/assets/{dagre-6UL2VRFP-C8ptQ9V3.js.map → dagre-6UL2VRFP-BcweuZHt.js.map} +1 -1
- package/dist/static/assets/{diagram-PSM6KHXK-Bgf1FqkE.js → diagram-PSM6KHXK-D4-QwLW1.js} +8 -8
- package/dist/static/assets/{diagram-PSM6KHXK-Bgf1FqkE.js.map → diagram-PSM6KHXK-D4-QwLW1.js.map} +1 -1
- package/dist/static/assets/{diagram-QEK2KX5R-BZ5xzofU.js → diagram-QEK2KX5R-BVbuejJn.js} +7 -7
- package/dist/static/assets/{diagram-QEK2KX5R-BZ5xzofU.js.map → diagram-QEK2KX5R-BVbuejJn.js.map} +1 -1
- package/dist/static/assets/{diagram-S2PKOQOG-Dwp47T9I.js → diagram-S2PKOQOG-pB6N6Tq_.js} +7 -7
- package/dist/static/assets/{diagram-S2PKOQOG-Dwp47T9I.js.map → diagram-S2PKOQOG-pB6N6Tq_.js.map} +1 -1
- package/dist/static/assets/{erDiagram-Q2GNP2WA-Cx4weIHl.js → erDiagram-Q2GNP2WA-DLKmthuw.js} +5 -5
- package/dist/static/assets/{erDiagram-Q2GNP2WA-Cx4weIHl.js.map → erDiagram-Q2GNP2WA-DLKmthuw.js.map} +1 -1
- package/dist/static/assets/{flowDiagram-NV44I4VS-vNUuIeRk.js → flowDiagram-NV44I4VS-BsBhWukh.js} +6 -6
- package/dist/static/assets/{flowDiagram-NV44I4VS-vNUuIeRk.js.map → flowDiagram-NV44I4VS-BsBhWukh.js.map} +1 -1
- package/dist/static/assets/{ganttDiagram-JELNMOA3-BEfozJAr.js → ganttDiagram-JELNMOA3-Debz-J-C.js} +3 -3
- package/dist/static/assets/{ganttDiagram-JELNMOA3-BEfozJAr.js.map → ganttDiagram-JELNMOA3-Debz-J-C.js.map} +1 -1
- package/dist/static/assets/{gitGraphDiagram-V2S2FVAM-eHxwc3d9.js → gitGraphDiagram-V2S2FVAM-BnAPFBGR.js} +8 -8
- package/dist/static/assets/{gitGraphDiagram-V2S2FVAM-eHxwc3d9.js.map → gitGraphDiagram-V2S2FVAM-BnAPFBGR.js.map} +1 -1
- package/dist/static/assets/{graph-C6a6uAok.js → graph-DbzWiBNK.js} +3 -3
- package/dist/static/assets/{graph-C6a6uAok.js.map → graph-DbzWiBNK.js.map} +1 -1
- package/dist/static/assets/{index-D3TQbAKh.js → index-B-8J28g7.js} +1096 -1079
- package/dist/static/assets/index-B-8J28g7.js.map +1 -0
- package/dist/static/assets/{index-BiNcBn_U.css → index-CD5wtC_i.css} +1 -1
- package/dist/static/assets/{infoDiagram-HS3SLOUP-CX0NiId3.js → infoDiagram-HS3SLOUP-CZ5hWoxV.js} +6 -6
- package/dist/static/assets/{infoDiagram-HS3SLOUP-CX0NiId3.js.map → infoDiagram-HS3SLOUP-CZ5hWoxV.js.map} +1 -1
- package/dist/static/assets/{journeyDiagram-XKPGCS4Q-C1IepPZ-.js → journeyDiagram-XKPGCS4Q-CKN3oSxk.js} +5 -5
- package/dist/static/assets/{journeyDiagram-XKPGCS4Q-C1IepPZ-.js.map → journeyDiagram-XKPGCS4Q-CKN3oSxk.js.map} +1 -1
- package/dist/static/assets/{kanban-definition-3W4ZIXB7-uMNX4Z1W.js → kanban-definition-3W4ZIXB7-BQCMklfJ.js} +3 -3
- package/dist/static/assets/{kanban-definition-3W4ZIXB7-uMNX4Z1W.js.map → kanban-definition-3W4ZIXB7-BQCMklfJ.js.map} +1 -1
- package/dist/static/assets/{layout-CpE3kk5z.js → layout-C5B58szc.js} +5 -5
- package/dist/static/assets/{layout-CpE3kk5z.js.map → layout-C5B58szc.js.map} +1 -1
- package/dist/static/assets/{linear-DV8laXr9.js → linear-_32fut6G.js} +2 -2
- package/dist/static/assets/{linear-DV8laXr9.js.map → linear-_32fut6G.js.map} +1 -1
- package/dist/static/assets/{mindmap-definition-VGOIOE7T-CKjgVM9S.js → mindmap-definition-VGOIOE7T-C_goMzjx.js} +4 -4
- package/dist/static/assets/{mindmap-definition-VGOIOE7T-CKjgVM9S.js.map → mindmap-definition-VGOIOE7T-C_goMzjx.js.map} +1 -1
- package/dist/static/assets/{pieDiagram-ADFJNKIX-BBonlNyT.js → pieDiagram-ADFJNKIX-BQ2n0cOB.js} +8 -8
- package/dist/static/assets/{pieDiagram-ADFJNKIX-BBonlNyT.js.map → pieDiagram-ADFJNKIX-BQ2n0cOB.js.map} +1 -1
- package/dist/static/assets/{quadrantDiagram-AYHSOK5B-BTI8HbBu.js → quadrantDiagram-AYHSOK5B-BLg7_neg.js} +3 -3
- package/dist/static/assets/{quadrantDiagram-AYHSOK5B-BTI8HbBu.js.map → quadrantDiagram-AYHSOK5B-BLg7_neg.js.map} +1 -1
- package/dist/static/assets/{requirementDiagram-UZGBJVZJ-ZtSr9Q5R.js → requirementDiagram-UZGBJVZJ-DwkJt0zi.js} +4 -4
- package/dist/static/assets/{requirementDiagram-UZGBJVZJ-ZtSr9Q5R.js.map → requirementDiagram-UZGBJVZJ-DwkJt0zi.js.map} +1 -1
- package/dist/static/assets/{sankeyDiagram-TZEHDZUN-DibLVGzg.js → sankeyDiagram-TZEHDZUN-DmxmatUB.js} +2 -2
- package/dist/static/assets/{sankeyDiagram-TZEHDZUN-DibLVGzg.js.map → sankeyDiagram-TZEHDZUN-DmxmatUB.js.map} +1 -1
- package/dist/static/assets/{sequenceDiagram-WL72ISMW-qXatfzVt.js → sequenceDiagram-WL72ISMW-KHU_eApU.js} +4 -4
- package/dist/static/assets/{sequenceDiagram-WL72ISMW-qXatfzVt.js.map → sequenceDiagram-WL72ISMW-KHU_eApU.js.map} +1 -1
- package/dist/static/assets/{stateDiagram-FKZM4ZOC-7fgxCQHo.js → stateDiagram-FKZM4ZOC-B3DBCxAL.js} +9 -9
- package/dist/static/assets/{stateDiagram-FKZM4ZOC-7fgxCQHo.js.map → stateDiagram-FKZM4ZOC-B3DBCxAL.js.map} +1 -1
- package/dist/static/assets/{stateDiagram-v2-4FDKWEC3-DcWlOAnF.js → stateDiagram-v2-4FDKWEC3-C-uIk7gh.js} +5 -5
- package/dist/static/assets/{stateDiagram-v2-4FDKWEC3-DcWlOAnF.js.map → stateDiagram-v2-4FDKWEC3-C-uIk7gh.js.map} +1 -1
- package/dist/static/assets/{timeline-definition-IT6M3QCI-iX2MRdpY.js → timeline-definition-IT6M3QCI-SysEcQCC.js} +3 -3
- package/dist/static/assets/{timeline-definition-IT6M3QCI-iX2MRdpY.js.map → timeline-definition-IT6M3QCI-SysEcQCC.js.map} +1 -1
- package/dist/static/assets/{treemap-GDKQZRPO-AVRnyXu1.js → treemap-GDKQZRPO-d0AbKEc4.js} +5 -5
- package/dist/static/assets/{treemap-GDKQZRPO-AVRnyXu1.js.map → treemap-GDKQZRPO-d0AbKEc4.js.map} +1 -1
- package/dist/static/assets/{xychartDiagram-PRI3JC2R-DVYEo5aJ.js → xychartDiagram-PRI3JC2R-CmSQMxUh.js} +3 -3
- package/dist/static/assets/{xychartDiagram-PRI3JC2R-DVYEo5aJ.js.map → xychartDiagram-PRI3JC2R-CmSQMxUh.js.map} +1 -1
- package/dist/static/index.html +2 -2
- package/dist/tools/ctrl.js +3 -3
- package/dist/tools/fs.js +1 -1
- package/dist/tools/prompts/control/en/index.md +5 -4
- package/dist/tools/prompts/control/en/principles.md +11 -7
- package/dist/tools/prompts/control/en/tools.md +19 -0
- package/dist/tools/prompts/control/zh/index.md +5 -4
- package/dist/tools/prompts/control/zh/principles.md +11 -7
- package/dist/tools/prompts/control/zh/tools.md +19 -0
- package/package.json +9 -7
- package/dist/agent-priming.js +0 -2051
- package/dist/apps/installed-file.js +0 -207
- package/dist/apps/runtime-port.js +0 -91
- package/dist/docs/dominds-agent-priming.md +0 -218
- package/dist/docs/dominds-agent-priming.zh.md +0 -196
- package/dist/docs/drive-logic-context-refactor-plan.zh.md +0 -338
- package/dist/docs/keep-going.md +0 -176
- package/dist/docs/keep-going.zh.md +0 -162
- package/dist/docs/kernel-app-architecture.md +0 -286
- package/dist/docs/kernel-app-architecture.zh.md +0 -285
- package/dist/docs/showing-by-doing.md +0 -208
- package/dist/docs/showing-by-doing.zh.md +0 -177
- package/dist/docs/team-mgmt-toolset.md +0 -482
- package/dist/docs/team-mgmt-toolset.zh.md +0 -426
- package/dist/llm/driver-entry.js +0 -28
- package/dist/llm/driver-v2/context-health.js +0 -121
- package/dist/llm/driver-v2/context.js +0 -56
- package/dist/llm/driver-v2/core.js +0 -1545
- package/dist/llm/driver-v2/index.js +0 -26
- package/dist/llm/driver-v2/orchestrator.js +0 -158
- package/dist/llm/driver-v2/policy.js +0 -129
- package/dist/llm/driver-v2/restore-dialog-hierarchy.js +0 -73
- package/dist/llm/driver-v2/round.js +0 -366
- package/dist/llm/driver-v2/runtime-utils.js +0 -365
- package/dist/llm/driver-v2/saying-events.js +0 -20
- package/dist/llm/driver-v2/subdialog-txn.js +0 -42
- package/dist/llm/driver-v2/supdialog-response.js +0 -400
- package/dist/llm/driver-v2/tellask-bridge.js +0 -1148
- package/dist/llm/driver-v2/types.js +0 -10
- package/dist/llm/driver-v2-ref-only/context-health.js +0 -121
- package/dist/llm/driver-v2-ref-only/context.js +0 -17
- package/dist/llm/driver-v2-ref-only/core.js +0 -1710
- package/dist/llm/driver-v2-ref-only/index.js +0 -26
- package/dist/llm/driver-v2-ref-only/orchestrator.js +0 -158
- package/dist/llm/driver-v2-ref-only/policy.js +0 -129
- package/dist/llm/driver-v2-ref-only/restore-dialog-hierarchy.js +0 -73
- package/dist/llm/driver-v2-ref-only/round.js +0 -366
- package/dist/llm/driver-v2-ref-only/runtime-utils.js +0 -473
- package/dist/llm/driver-v2-ref-only/saying-events.js +0 -18
- package/dist/llm/driver-v2-ref-only/subdialog-txn.js +0 -42
- package/dist/llm/driver-v2-ref-only/supdialog-response.js +0 -453
- package/dist/llm/driver-v2-ref-only/tellask-bridge.js +0 -1178
- package/dist/llm/driver-v2-ref-only/types.js +0 -10
- package/dist/llm/driver.js +0 -4093
- package/dist/minds/promptdocs.js +0 -263
- package/dist/server/prompts-routes.js +0 -545
- package/dist/shared/team-mgmt-manual.js +0 -120
- package/dist/shared/types/prompts.js +0 -2
- package/dist/shared/types/tellask.js +0 -8
- package/dist/showing-by-doing.js +0 -1091
- package/dist/snippets/README.en.md +0 -3
- package/dist/snippets/README.md +0 -4
- package/dist/static/assets/index-D3TQbAKh.js.map +0 -1
- package/dist/tellask.js +0 -439
- package/dist/tools/context-health.js +0 -177
- package/dist/tools/diag.js +0 -583
- package/dist/tools/prompts/memory/en/errors.md +0 -155
- package/dist/tools/prompts/memory/en/index.md +0 -47
- package/dist/tools/prompts/memory/en/principles.md +0 -87
- package/dist/tools/prompts/memory/en/scenarios.md +0 -174
- package/dist/tools/prompts/memory/en/tools.md +0 -129
- package/dist/tools/prompts/memory/zh/errors.md +0 -155
- package/dist/tools/prompts/memory/zh/index.md +0 -47
- package/dist/tools/prompts/memory/zh/principles.md +0 -89
- package/dist/tools/prompts/memory/zh/scenarios.md +0 -174
- package/dist/tools/prompts/memory/zh/tools.md +0 -129
- package/dist/tools/team-mgmt.js +0 -3487
- package/dist/utils/task-doc.js +0 -236
package/README.md
CHANGED
|
@@ -69,9 +69,16 @@ Dominds is an AI-powered DevOps framework that creates autonomous agentic teams
|
|
|
69
69
|
|
|
70
70
|
### Prerequisites
|
|
71
71
|
|
|
72
|
-
- **Node.js (with npm bundled)**: Version
|
|
72
|
+
- **Node.js (with npm bundled)**: Version 24.x LTS
|
|
73
|
+
- **npm (if used directly)**: Use the npm bundled with your installed Node.js 24 LTS; current minimum in this repo is `11.9.0`, and newer versions are allowed.
|
|
73
74
|
- **LLM provider configured for your team**: Dominds ships with a built-in provider catalog [main/llm/defaults.yaml](./main/llm/defaults.yaml) including Codex (ChatGPT) and Anthropic, plus several Anthropic-compatible endpoints (e.g. MiniMax, Z.ai, BigModel). You’ll need valid credentials for at least one provider.
|
|
74
|
-
- **pnpm (optional)**: Recommended only if you’re developing Dominds itself.
|
|
75
|
+
- **pnpm (optional)**: Recommended only if you’re developing Dominds itself. Prefer enabling the pinned CLI once via `npm run setup:pm`. If Corepack shims cannot be enabled in your environment, install `pnpm@10` manually.
|
|
76
|
+
|
|
77
|
+
For repo development, initialize the package manager once after `nvm use --lts`:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
npm run setup:pm
|
|
81
|
+
```
|
|
75
82
|
|
|
76
83
|
### Install Dominds
|
|
77
84
|
|
package/README.zh.md
CHANGED
|
@@ -23,9 +23,16 @@ Dominds 是一款面向开发运作(DevOps)场景的智能体框架,它将
|
|
|
23
23
|
|
|
24
24
|
### 环境要求
|
|
25
25
|
|
|
26
|
-
- **Node.js(含 npm)**:版本
|
|
26
|
+
- **Node.js(含 npm)**:版本 24.x LTS
|
|
27
|
+
- **npm(如直接使用)**:使用当前安装的 Node.js 24 LTS 自带 npm;本仓库当前最低要求为 `11.9.0`,更高版本也允许。
|
|
27
28
|
- **至少一个可用的 LLM 服务提供商**:Dominds 内置提供商目录(路径:[main/llm/defaults.yaml](./main/llm/defaults.yaml)),需为其中至少一个提供商配置有效凭证(通过环境变量设置)。
|
|
28
|
-
- **pnpm(可选)**:仅在开发 Dominds
|
|
29
|
+
- **pnpm(可选)**:仅在开发 Dominds 本体时推荐使用。优先执行一次 `npm run setup:pm` 启用并缓存仓库固定的 pnpm;若你的环境无法启用 Corepack shim,再手动安装 `pnpm@10`。
|
|
30
|
+
|
|
31
|
+
开发本仓库时,建议在 `nvm use --lts` 之后先执行一次:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm run setup:pm
|
|
35
|
+
```
|
|
29
36
|
|
|
30
37
|
### 安装 Dominds
|
|
31
38
|
|
|
@@ -6,7 +6,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.APP_LOCK_REL_PATH = void 0;
|
|
7
7
|
exports.parseAppLockFile = parseAppLockFile;
|
|
8
8
|
exports.loadAppLockFile = loadAppLockFile;
|
|
9
|
+
exports.findLockedApp = findLockedApp;
|
|
9
10
|
exports.upsertLockedApp = upsertLockedApp;
|
|
11
|
+
exports.removeLockedApp = removeLockedApp;
|
|
10
12
|
exports.writeAppLockFileIfChanged = writeAppLockFileIfChanged;
|
|
11
13
|
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
12
14
|
const node_path_1 = __importDefault(require("node:path"));
|
|
@@ -24,53 +26,18 @@ function asNullableString(v) {
|
|
|
24
26
|
return null;
|
|
25
27
|
return typeof v === 'string' ? v : null;
|
|
26
28
|
}
|
|
27
|
-
function parseSource(v, at) {
|
|
28
|
-
if (!isRecord(v))
|
|
29
|
-
return { ok: false, errorText: `Invalid ${at}: expected object` };
|
|
30
|
-
const kind = asString(v['kind']);
|
|
31
|
-
if (kind !== 'npx' && kind !== 'local') {
|
|
32
|
-
return { ok: false, errorText: `Invalid ${at}.kind: expected 'npx'|'local'` };
|
|
33
|
-
}
|
|
34
|
-
if (kind === 'npx') {
|
|
35
|
-
const keys = Object.keys(v);
|
|
36
|
-
for (const k of keys) {
|
|
37
|
-
if (k !== 'kind' && k !== 'spec') {
|
|
38
|
-
return { ok: false, errorText: `Invalid ${at}: unknown key '${k}'` };
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
const spec = asString(v['spec']);
|
|
42
|
-
if (!spec || spec.trim() === '') {
|
|
43
|
-
return { ok: false, errorText: `Invalid ${at}.spec: required` };
|
|
44
|
-
}
|
|
45
|
-
return { ok: true, source: { kind, spec } };
|
|
46
|
-
}
|
|
47
|
-
const keys = Object.keys(v);
|
|
48
|
-
for (const k of keys) {
|
|
49
|
-
if (k !== 'kind' && k !== 'pathAbs') {
|
|
50
|
-
return { ok: false, errorText: `Invalid ${at}: unknown key '${k}'` };
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
const pathAbs = asString(v['pathAbs']);
|
|
54
|
-
if (!pathAbs || pathAbs.trim() === '') {
|
|
55
|
-
return { ok: false, errorText: `Invalid ${at}.pathAbs: required` };
|
|
56
|
-
}
|
|
57
|
-
return { ok: true, source: { kind, pathAbs } };
|
|
58
|
-
}
|
|
59
29
|
function parseEntry(v, at) {
|
|
60
30
|
if (!isRecord(v))
|
|
61
31
|
return { ok: false, errorText: `Invalid ${at}: expected object` };
|
|
62
32
|
const keys = Object.keys(v);
|
|
63
33
|
for (const k of keys) {
|
|
64
|
-
if (k !== 'id' && k !== '
|
|
34
|
+
if (k !== 'id' && k !== 'package') {
|
|
65
35
|
return { ok: false, errorText: `Invalid ${at}: unknown key '${k}'` };
|
|
66
36
|
}
|
|
67
37
|
}
|
|
68
38
|
const id = asString(v['id']);
|
|
69
39
|
if (!id || id.trim() === '')
|
|
70
40
|
return { ok: false, errorText: `Invalid ${at}.id: required` };
|
|
71
|
-
const sourceParsed = parseSource(v['source'], `${at}.source`);
|
|
72
|
-
if (!sourceParsed.ok)
|
|
73
|
-
return sourceParsed;
|
|
74
41
|
const pkg = v['package'];
|
|
75
42
|
if (!isRecord(pkg)) {
|
|
76
43
|
return { ok: false, errorText: `Invalid ${at}.package: expected object` };
|
|
@@ -98,22 +65,23 @@ function parseEntry(v, at) {
|
|
|
98
65
|
return {
|
|
99
66
|
ok: true,
|
|
100
67
|
entry: {
|
|
101
|
-
id,
|
|
102
|
-
|
|
103
|
-
package: { name, version },
|
|
68
|
+
id: id.trim(),
|
|
69
|
+
package: { name: name.trim(), version },
|
|
104
70
|
},
|
|
105
71
|
};
|
|
106
72
|
}
|
|
107
73
|
function canonicalizeLockFile(file) {
|
|
108
74
|
const byId = new Map();
|
|
109
|
-
for (const
|
|
110
|
-
if (byId.has(
|
|
111
|
-
throw new Error(`Invalid ${exports.APP_LOCK_REL_PATH}: duplicate app id '${
|
|
75
|
+
for (const entry of file.apps) {
|
|
76
|
+
if (byId.has(entry.id)) {
|
|
77
|
+
throw new Error(`Invalid ${exports.APP_LOCK_REL_PATH}: duplicate app id '${entry.id}'`);
|
|
112
78
|
}
|
|
113
|
-
byId.set(
|
|
79
|
+
byId.set(entry.id, entry);
|
|
114
80
|
}
|
|
115
|
-
|
|
116
|
-
|
|
81
|
+
return {
|
|
82
|
+
schemaVersion: 1,
|
|
83
|
+
apps: [...byId.values()].sort((a, b) => a.id.localeCompare(b.id)),
|
|
84
|
+
};
|
|
117
85
|
}
|
|
118
86
|
function parseAppLockFile(parsed, filePathAbs) {
|
|
119
87
|
if (!isRecord(parsed)) {
|
|
@@ -141,10 +109,10 @@ function parseAppLockFile(parsed, filePathAbs) {
|
|
|
141
109
|
}
|
|
142
110
|
const apps = [];
|
|
143
111
|
for (let i = 0; i < appsRaw.length; i += 1) {
|
|
144
|
-
const
|
|
145
|
-
if (!
|
|
146
|
-
return { ok: false, errorText: `${
|
|
147
|
-
apps.push(
|
|
112
|
+
const parsedEntry = parseEntry(appsRaw[i], `apps[${i}]`);
|
|
113
|
+
if (!parsedEntry.ok)
|
|
114
|
+
return { ok: false, errorText: `${parsedEntry.errorText} (${filePathAbs})` };
|
|
115
|
+
apps.push(parsedEntry.entry);
|
|
148
116
|
}
|
|
149
117
|
try {
|
|
150
118
|
return { ok: true, file: canonicalizeLockFile({ schemaVersion: 1, apps }) };
|
|
@@ -192,33 +160,41 @@ async function loadAppLockFile(params) {
|
|
|
192
160
|
return { kind: 'error', filePathAbs, errorText: lockParsed.errorText };
|
|
193
161
|
return { kind: 'ok', filePathAbs, exists: true, file: lockParsed.file };
|
|
194
162
|
}
|
|
163
|
+
function findLockedApp(file, appId) {
|
|
164
|
+
return file.apps.find((entry) => entry.id === appId) ?? null;
|
|
165
|
+
}
|
|
195
166
|
function upsertLockedApp(params) {
|
|
196
|
-
const existingEntry = params.existing
|
|
167
|
+
const existingEntry = findLockedApp(params.existing, params.next.id);
|
|
197
168
|
if (existingEntry && (0, node_util_1.isDeepStrictEqual)(existingEntry, params.next))
|
|
198
169
|
return params.existing;
|
|
199
170
|
const apps = [...params.existing.apps];
|
|
200
|
-
const idx = apps.findIndex((
|
|
171
|
+
const idx = apps.findIndex((entry) => entry.id === params.next.id);
|
|
201
172
|
if (idx >= 0)
|
|
202
173
|
apps[idx] = params.next;
|
|
203
174
|
else
|
|
204
175
|
apps.push(params.next);
|
|
205
176
|
return canonicalizeLockFile({ schemaVersion: 1, apps });
|
|
206
177
|
}
|
|
178
|
+
function removeLockedApp(params) {
|
|
179
|
+
if (!params.existing.apps.some((entry) => entry.id === params.appId))
|
|
180
|
+
return params.existing;
|
|
181
|
+
return canonicalizeLockFile({
|
|
182
|
+
schemaVersion: 1,
|
|
183
|
+
apps: params.existing.apps.filter((entry) => entry.id !== params.appId),
|
|
184
|
+
});
|
|
185
|
+
}
|
|
207
186
|
async function writeAppLockFileIfChanged(params) {
|
|
208
187
|
const canonical = canonicalizeLockFile(params.file);
|
|
209
188
|
const loaded = await loadAppLockFile({ rtwsRootAbs: params.rtwsRootAbs });
|
|
210
189
|
if (loaded.kind === 'error') {
|
|
211
|
-
// Avoid overwriting user edits if the existing file is corrupt.
|
|
212
190
|
throw new Error(`Failed to read ${exports.APP_LOCK_REL_PATH}: ${loaded.errorText}`);
|
|
213
191
|
}
|
|
214
192
|
if (loaded.exists) {
|
|
215
|
-
|
|
216
|
-
if ((0, node_util_1.isDeepStrictEqual)(prevCanonical, canonical))
|
|
193
|
+
if ((0, node_util_1.isDeepStrictEqual)(canonicalizeLockFile(loaded.file), canonical))
|
|
217
194
|
return;
|
|
218
195
|
}
|
|
219
|
-
else {
|
|
220
|
-
|
|
221
|
-
return;
|
|
196
|
+
else if (canonical.apps.length === 0) {
|
|
197
|
+
return;
|
|
222
198
|
}
|
|
223
199
|
const mindsDirAbs = node_path_1.default.resolve(params.rtwsRootAbs, '.minds');
|
|
224
200
|
await promises_1.default.mkdir(mindsDirAbs, { recursive: true });
|
|
@@ -0,0 +1,267 @@
|
|
|
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.APPS_CONFIGURATION_REL_PATH = exports.DEFAULT_APPS_RESOLUTION_STRATEGY = void 0;
|
|
7
|
+
exports.parseResolutionStrategy = parseResolutionStrategy;
|
|
8
|
+
exports.loadAppsConfigurationFile = loadAppsConfigurationFile;
|
|
9
|
+
exports.writeAppsConfigurationFile = writeAppsConfigurationFile;
|
|
10
|
+
exports.writeAppsConfigurationFileIfChanged = writeAppsConfigurationFileIfChanged;
|
|
11
|
+
exports.isAppDisabledInConfiguration = isAppDisabledInConfiguration;
|
|
12
|
+
exports.setAppDisabledInConfiguration = setAppDisabledInConfiguration;
|
|
13
|
+
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
14
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
15
|
+
const yaml_1 = __importDefault(require("yaml"));
|
|
16
|
+
exports.DEFAULT_APPS_RESOLUTION_STRATEGY = {
|
|
17
|
+
order: ['local'],
|
|
18
|
+
localRoots: ['dominds-apps'],
|
|
19
|
+
};
|
|
20
|
+
exports.APPS_CONFIGURATION_REL_PATH = node_path_1.default.join('.apps', 'configuration.yaml');
|
|
21
|
+
function isRecord(v) {
|
|
22
|
+
return typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
23
|
+
}
|
|
24
|
+
function asString(v) {
|
|
25
|
+
return typeof v === 'string' ? v : null;
|
|
26
|
+
}
|
|
27
|
+
function parseResolutionStrategy(v, at) {
|
|
28
|
+
if (!isRecord(v))
|
|
29
|
+
return { ok: false, errorText: `Invalid ${at}: expected object` };
|
|
30
|
+
const keys = Object.keys(v);
|
|
31
|
+
for (const k of keys) {
|
|
32
|
+
if (k !== 'order' && k !== 'localRoots') {
|
|
33
|
+
return { ok: false, errorText: `Invalid ${at}: unknown key '${k}'` };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const orderRaw = v['order'];
|
|
37
|
+
const order = (() => {
|
|
38
|
+
if (orderRaw === undefined)
|
|
39
|
+
return undefined;
|
|
40
|
+
if (!Array.isArray(orderRaw)) {
|
|
41
|
+
return { ok: false, errorText: `Invalid ${at}.order: expected array` };
|
|
42
|
+
}
|
|
43
|
+
const out = [];
|
|
44
|
+
for (let i = 0; i < orderRaw.length; i += 1) {
|
|
45
|
+
const item = orderRaw[i];
|
|
46
|
+
if (item !== 'local' && item !== 'npx') {
|
|
47
|
+
return {
|
|
48
|
+
ok: false,
|
|
49
|
+
errorText: `Invalid ${at}.order[${i}]: expected 'local'|'npx'`,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
out.push(item);
|
|
53
|
+
}
|
|
54
|
+
if (out.length === 0) {
|
|
55
|
+
return { ok: false, errorText: `Invalid ${at}.order: must not be empty` };
|
|
56
|
+
}
|
|
57
|
+
return { ok: true, value: out };
|
|
58
|
+
})();
|
|
59
|
+
if (order && !order.ok)
|
|
60
|
+
return order;
|
|
61
|
+
const localRootsRaw = v['localRoots'];
|
|
62
|
+
const localRoots = (() => {
|
|
63
|
+
if (localRootsRaw === undefined)
|
|
64
|
+
return undefined;
|
|
65
|
+
if (!Array.isArray(localRootsRaw)) {
|
|
66
|
+
return { ok: false, errorText: `Invalid ${at}.localRoots: expected array` };
|
|
67
|
+
}
|
|
68
|
+
const out = [];
|
|
69
|
+
for (let i = 0; i < localRootsRaw.length; i += 1) {
|
|
70
|
+
const item = localRootsRaw[i];
|
|
71
|
+
if (typeof item !== 'string' || item.trim() === '') {
|
|
72
|
+
return {
|
|
73
|
+
ok: false,
|
|
74
|
+
errorText: `Invalid ${at}.localRoots[${i}]: expected non-empty string`,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
out.push(item.trim());
|
|
78
|
+
}
|
|
79
|
+
if (out.length === 0) {
|
|
80
|
+
return { ok: false, errorText: `Invalid ${at}.localRoots: must not be empty` };
|
|
81
|
+
}
|
|
82
|
+
return { ok: true, value: out };
|
|
83
|
+
})();
|
|
84
|
+
if (localRoots && !localRoots.ok)
|
|
85
|
+
return localRoots;
|
|
86
|
+
return {
|
|
87
|
+
ok: true,
|
|
88
|
+
strategy: {
|
|
89
|
+
order: order ? order.value : undefined,
|
|
90
|
+
localRoots: localRoots ? localRoots.value : undefined,
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function canonicalizeConfigurationFile(file) {
|
|
95
|
+
const disabledApps = file.disabledApps
|
|
96
|
+
? [
|
|
97
|
+
...new Set(file.disabledApps.map((appId) => appId.trim()).filter((appId) => appId !== '')),
|
|
98
|
+
].sort()
|
|
99
|
+
: [];
|
|
100
|
+
return {
|
|
101
|
+
schemaVersion: 1,
|
|
102
|
+
resolutionStrategy: file.resolutionStrategy,
|
|
103
|
+
disabledApps: disabledApps.length > 0 ? disabledApps : undefined,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
function parseConfigurationFile(parsed, filePathAbs) {
|
|
107
|
+
if (!isRecord(parsed)) {
|
|
108
|
+
return { ok: false, errorText: `Invalid configuration.yaml: expected object (${filePathAbs})` };
|
|
109
|
+
}
|
|
110
|
+
const keys = Object.keys(parsed);
|
|
111
|
+
for (const k of keys) {
|
|
112
|
+
if (k !== 'schemaVersion' && k !== 'resolutionStrategy' && k !== 'disabledApps') {
|
|
113
|
+
return {
|
|
114
|
+
ok: false,
|
|
115
|
+
errorText: `Invalid configuration.yaml: unknown key '${k}' (${filePathAbs})`,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const schemaVersion = parsed['schemaVersion'];
|
|
120
|
+
if (schemaVersion !== 1) {
|
|
121
|
+
return {
|
|
122
|
+
ok: false,
|
|
123
|
+
errorText: `Unsupported configuration.yaml schemaVersion: ${String(schemaVersion)} (${filePathAbs})`,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
const strategyRaw = parsed['resolutionStrategy'];
|
|
127
|
+
const resolutionStrategy = (() => {
|
|
128
|
+
if (strategyRaw === undefined)
|
|
129
|
+
return undefined;
|
|
130
|
+
const strategy = parseResolutionStrategy(strategyRaw, 'resolutionStrategy');
|
|
131
|
+
if (!strategy.ok)
|
|
132
|
+
return strategy;
|
|
133
|
+
return { ok: true, value: strategy.strategy };
|
|
134
|
+
})();
|
|
135
|
+
if (resolutionStrategy && !resolutionStrategy.ok) {
|
|
136
|
+
return { ok: false, errorText: `${resolutionStrategy.errorText} (${filePathAbs})` };
|
|
137
|
+
}
|
|
138
|
+
const disabledRaw = parsed['disabledApps'];
|
|
139
|
+
const disabledApps = (() => {
|
|
140
|
+
if (disabledRaw === undefined)
|
|
141
|
+
return undefined;
|
|
142
|
+
if (!Array.isArray(disabledRaw)) {
|
|
143
|
+
return {
|
|
144
|
+
ok: false,
|
|
145
|
+
errorText: `Invalid disabledApps: expected array (${filePathAbs})`,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
const out = [];
|
|
149
|
+
for (let i = 0; i < disabledRaw.length; i += 1) {
|
|
150
|
+
const item = asString(disabledRaw[i]);
|
|
151
|
+
if (!item || item.trim() === '') {
|
|
152
|
+
return {
|
|
153
|
+
ok: false,
|
|
154
|
+
errorText: `Invalid disabledApps[${i}]: expected non-empty string (${filePathAbs})`,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
out.push(item.trim());
|
|
158
|
+
}
|
|
159
|
+
return { ok: true, value: out };
|
|
160
|
+
})();
|
|
161
|
+
if (disabledApps && !disabledApps.ok)
|
|
162
|
+
return disabledApps;
|
|
163
|
+
return {
|
|
164
|
+
ok: true,
|
|
165
|
+
file: canonicalizeConfigurationFile({
|
|
166
|
+
schemaVersion: 1,
|
|
167
|
+
resolutionStrategy: resolutionStrategy ? resolutionStrategy.value : undefined,
|
|
168
|
+
disabledApps: disabledApps ? disabledApps.value : undefined,
|
|
169
|
+
}),
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
async function loadAppsConfigurationFile(params) {
|
|
173
|
+
const filePathAbs = node_path_1.default.resolve(params.rtwsRootAbs, exports.APPS_CONFIGURATION_REL_PATH);
|
|
174
|
+
let raw;
|
|
175
|
+
try {
|
|
176
|
+
raw = await promises_1.default.readFile(filePathAbs, 'utf-8');
|
|
177
|
+
}
|
|
178
|
+
catch (err) {
|
|
179
|
+
const isEnoent = typeof err === 'object' &&
|
|
180
|
+
err !== null &&
|
|
181
|
+
'code' in err &&
|
|
182
|
+
err.code === 'ENOENT';
|
|
183
|
+
if (isEnoent) {
|
|
184
|
+
return {
|
|
185
|
+
kind: 'ok',
|
|
186
|
+
filePathAbs,
|
|
187
|
+
exists: false,
|
|
188
|
+
file: { schemaVersion: 1 },
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
return {
|
|
192
|
+
kind: 'error',
|
|
193
|
+
filePathAbs,
|
|
194
|
+
errorText: err instanceof Error ? err.message : String(err),
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
let parsed;
|
|
198
|
+
try {
|
|
199
|
+
parsed = yaml_1.default.parse(raw);
|
|
200
|
+
}
|
|
201
|
+
catch (err) {
|
|
202
|
+
return {
|
|
203
|
+
kind: 'error',
|
|
204
|
+
filePathAbs,
|
|
205
|
+
errorText: `Failed to parse YAML: ${err instanceof Error ? err.message : String(err)}`,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
const cfg = parseConfigurationFile(parsed, filePathAbs);
|
|
209
|
+
if (!cfg.ok) {
|
|
210
|
+
return { kind: 'error', filePathAbs, errorText: cfg.errorText };
|
|
211
|
+
}
|
|
212
|
+
return {
|
|
213
|
+
kind: 'ok',
|
|
214
|
+
filePathAbs,
|
|
215
|
+
exists: true,
|
|
216
|
+
file: cfg.file,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
async function writeAppsConfigurationFile(params) {
|
|
220
|
+
const dirAbs = node_path_1.default.resolve(params.rtwsRootAbs, '.apps');
|
|
221
|
+
await promises_1.default.mkdir(dirAbs, { recursive: true });
|
|
222
|
+
const filePathAbs = node_path_1.default.resolve(params.rtwsRootAbs, exports.APPS_CONFIGURATION_REL_PATH);
|
|
223
|
+
const yamlText = yaml_1.default.stringify(canonicalizeConfigurationFile(params.file));
|
|
224
|
+
await promises_1.default.writeFile(filePathAbs, yamlText, 'utf-8');
|
|
225
|
+
}
|
|
226
|
+
async function writeAppsConfigurationFileIfChanged(params) {
|
|
227
|
+
const dirAbs = node_path_1.default.resolve(params.rtwsRootAbs, '.apps');
|
|
228
|
+
await promises_1.default.mkdir(dirAbs, { recursive: true });
|
|
229
|
+
const filePathAbs = node_path_1.default.resolve(params.rtwsRootAbs, exports.APPS_CONFIGURATION_REL_PATH);
|
|
230
|
+
const yamlText = yaml_1.default.stringify(canonicalizeConfigurationFile(params.file));
|
|
231
|
+
try {
|
|
232
|
+
const prev = await promises_1.default.readFile(filePathAbs, 'utf-8');
|
|
233
|
+
if (prev === yamlText)
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
catch (err) {
|
|
237
|
+
const isEnoent = typeof err === 'object' &&
|
|
238
|
+
err !== null &&
|
|
239
|
+
'code' in err &&
|
|
240
|
+
err.code === 'ENOENT';
|
|
241
|
+
if (!isEnoent)
|
|
242
|
+
throw err;
|
|
243
|
+
}
|
|
244
|
+
await promises_1.default.writeFile(filePathAbs, yamlText, 'utf-8');
|
|
245
|
+
}
|
|
246
|
+
function isAppDisabledInConfiguration(file, appId) {
|
|
247
|
+
return (file.disabledApps ?? []).includes(appId);
|
|
248
|
+
}
|
|
249
|
+
function setAppDisabledInConfiguration(params) {
|
|
250
|
+
const appId = params.appId.trim();
|
|
251
|
+
if (appId === '')
|
|
252
|
+
return params.existing;
|
|
253
|
+
const disabledSet = new Set(params.existing.disabledApps ?? []);
|
|
254
|
+
if (params.disabled)
|
|
255
|
+
disabledSet.add(appId);
|
|
256
|
+
else
|
|
257
|
+
disabledSet.delete(appId);
|
|
258
|
+
const next = canonicalizeConfigurationFile({
|
|
259
|
+
schemaVersion: 1,
|
|
260
|
+
resolutionStrategy: params.existing.resolutionStrategy,
|
|
261
|
+
disabledApps: [...disabledSet],
|
|
262
|
+
});
|
|
263
|
+
const prev = canonicalizeConfigurationFile(params.existing);
|
|
264
|
+
if (yaml_1.default.stringify(prev) === yaml_1.default.stringify(next))
|
|
265
|
+
return params.existing;
|
|
266
|
+
return next;
|
|
267
|
+
}
|