@retailcrm/embed-ui-v1-endpoint 0.9.24 → 0.9.25

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 CHANGED
@@ -85,6 +85,15 @@ npx @retailcrm/embed-ui-v1-endpoint init-agents
85
85
  такого блока там ещё нет. С `--force` можно обновить уже существующий блок
86
86
  пакета.
87
87
 
88
+ Для project-level skills можно создать `.agents/skills/embed-ui-v1-endpoint-runtime/SKILL.md`:
89
+
90
+ ```bash
91
+ npx @retailcrm/embed-ui-v1-endpoint init-skills
92
+ ```
93
+
94
+ Skill описывает повторяемый workflow для pages/widgets, runner setup, target selection,
95
+ menu hierarchy, page publishing payloads и публичных границ Host API.
96
+
88
97
  ## Инициализация MCP-конфига
89
98
 
90
99
  Пакет также может сам добавить project-level MCP-настройки в целевой проект:
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ import { fileURLToPath } from 'node:url'
3
4
  import fs from 'node:fs'
4
5
  import path from 'node:path'
5
6
  import process from 'node:process'
@@ -13,6 +14,8 @@ const README_MCP_SECTION_HEADER = '## MCP For AI Assistants: @retailcrm/embed-ui
13
14
  const LEGACY_README_MCP_SECTION_HEADER = '## MCP For AI Assistants'
14
15
  const README_MCP_MARKER = 'embed-ui-v1-endpoint://targets'
15
16
  const MCP_SERVER_NAME = 'retailcrm-embed-ui-v1-endpoint'
17
+ const SKILL_NAME = 'embed-ui-v1-endpoint-runtime'
18
+ const SKILL_TEMPLATE_PATH = `templates/skills/${SKILL_NAME}/SKILL.md.txt`
16
19
  const MCP_BIN_NAME = process.platform === 'win32' ? 'embed-ui-v1-endpoint-mcp.cmd' : 'embed-ui-v1-endpoint-mcp'
17
20
  const RELATIVE_MCP_BIN_PATH = `./node_modules/.bin/${MCP_BIN_NAME}`
18
21
  const CLAUDE_PROJECT_MCP_BIN_PATH = `\${CLAUDE_PROJECT_DIR:-.}/node_modules/.bin/${MCP_BIN_NAME}`
@@ -49,6 +52,7 @@ const MCP_CLIENT_CONFIGS = {
49
52
  const HELP_TEXT = `Usage:
50
53
  npx ${PACKAGE_NAME} init-agents [target] [options]
51
54
  npx ${PACKAGE_NAME} init-config [target] [options]
55
+ npx ${PACKAGE_NAME} init-skills [target] [options]
52
56
 
53
57
  Options:
54
58
  -f, --force Replace existing managed sections and MCP server entries
@@ -58,6 +62,7 @@ Options:
58
62
 
59
63
  Examples:
60
64
  npx ${PACKAGE_NAME} init-agents
65
+ npx ${PACKAGE_NAME} init-skills
61
66
  npx ${PACKAGE_NAME} init-agents ./my-project
62
67
  npx ${PACKAGE_NAME} init-agents --force
63
68
  npx ${PACKAGE_NAME} init-config ./my-project
@@ -75,6 +80,10 @@ const printMcpNotice = (message) => {
75
80
  console.log(`MCP: ${message}`)
76
81
  }
77
82
 
83
+ const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
84
+
85
+ const createSkill = () => fs.readFileSync(path.join(packageRoot, SKILL_TEMPLATE_PATH), 'utf8')
86
+
78
87
  const parseArgs = (argv) => {
79
88
  const options = {
80
89
  command: null,
@@ -457,6 +466,35 @@ const initAgents = (target, force) => {
457
466
  console.log(`The ${PACKAGE_NAME} instructions were appended to the end of the file.`)
458
467
  }
459
468
 
469
+ const initSkills = (target, options) => {
470
+ if (!fs.existsSync(target)) {
471
+ throw new Error(`Target path does not exist: ${target}`)
472
+ }
473
+
474
+ const stat = fs.statSync(target)
475
+
476
+ if (!stat.isDirectory()) {
477
+ throw new Error(`Target path is not a directory: ${target}`)
478
+ }
479
+
480
+ const skillPath = path.join(target, '.agents', 'skills', SKILL_NAME, 'SKILL.md')
481
+ const fileExists = fs.existsSync(skillPath)
482
+
483
+ if (fileExists && !options.force) {
484
+ console.log(`${skillPath} already exists`)
485
+ console.log('Nothing was changed. Re-run with --force to refresh that skill.')
486
+ return
487
+ }
488
+
489
+ if (!options.dryRun) {
490
+ fs.mkdirSync(path.dirname(skillPath), { recursive: true })
491
+ fs.writeFileSync(skillPath, createSkill(), 'utf8')
492
+ }
493
+
494
+ const action = fileExists ? 'updated' : 'created'
495
+ console.log(`SKILL: ${options.dryRun ? `would ${action}` : action} ${skillPath}`)
496
+ }
497
+
460
498
  const writeMcpServerConfig = (target, relativePath, rootField, options, serverConfig) => {
461
499
  const filePath = path.join(target, relativePath)
462
500
  const fileExists = fs.existsSync(filePath)
@@ -573,6 +611,11 @@ const main = () => {
573
611
  return
574
612
  }
575
613
 
614
+ if (options.command === 'init-skills') {
615
+ initSkills(options.target, options)
616
+ return
617
+ }
618
+
576
619
  throw new Error(`Unknown command: ${options.command}`)
577
620
  } catch (error) {
578
621
  console.error(error instanceof Error ? error.message : String(error))
package/docs/layout.md CHANGED
@@ -31,6 +31,8 @@
31
31
  - Для контентных подложек базовое правило такое:
32
32
  - отступ от верхнего края подложки: `24px`;
33
33
  - отступы от левого, правого и нижнего края: `32px`.
34
+ - Эти отступы относятся к белой контентной подложке, а не к корневому wrapper страницы.
35
+ - Корневой wrapper страницы не должен дублировать padding подложки; внешнюю рамку страницы задаёт CRM host layout.
34
36
  - Между смысловыми блоками оставляйте заметную дистанцию, а не склеивайте их.
35
37
 
36
38
  ### Заголовок страницы
@@ -65,6 +67,7 @@
65
67
 
66
68
  - Основной контент страницы обычно расположен на белых подложках.
67
69
  - Подложка должна собирать один смысловой блок, а не случайный набор элементов.
70
+ - Padding `24px 32px 32px` ставится на саму подложку с контентом, а не на общий wrapper страницы.
68
71
  - Если на экране есть несколько независимых смысловых секций, предпочтительнее использовать несколько подложек, чем одну перегруженную.
69
72
 
70
73
  ## 1. Страница со списком сущностей
@@ -103,6 +106,8 @@
103
106
 
104
107
  - Таблица является главным содержимым списка.
105
108
  - Она может листаться, пагинироваться и поддерживать выгрузку.
109
+ - `UiTable` не нужно заворачивать в дополнительную белую подложку: таблица сама задаёт табличную поверхность, границы и внутренние отступы.
110
+ - Wrapper вокруг таблицы допустим только как layout/scroll/width container без собственной белой карточки и без padding подложки.
106
111
  - Не следует смешивать в одном списочном экране слишком много “карточной” логики; список должен оставаться в первую очередь инструментом поиска и просмотра.
107
112
 
108
113
  Типичная компонентная основа:
@@ -337,6 +342,7 @@
337
342
  4. Нужна ли общая панель сохранения, или каждый блок должен сохраняться отдельно?
338
343
  5. Есть ли на странице не больше одного page-level `primary`-действия каждого варианта, а дополнительные `Default Primary` находятся только внутри отдельных `UiCollapseBox`?
339
344
  6. Держатся ли отступы и расстояния сетки `4px`?
345
+ 7. Не продублирован ли padding контентной подложки на корневом wrapper страницы?
340
346
 
341
347
  ---
342
348
 
@@ -61,6 +61,44 @@
61
61
  `menu: "private_main_menu"` и `parentMenuItemCode: "settings"`. Для страниц в разделе продаж используйте
62
62
  `menu: "activity_main_menu"` и соответствующий parent menu item, например `orders`.
63
63
 
64
+ ## Payload публикации
65
+
66
+ При обновлении уже установленного расширения синхронизируйте существующий integration module через
67
+ `/api/v5/integration-modules/{code}/edit`, а не создавайте новый инстанс регистрации. В payload страницы
68
+ должны находиться внутри `integrationModule.integrations.embedJs.pages`:
69
+
70
+ ```json
71
+ {
72
+ "integrationModule": {
73
+ "clientId": "client-id-xxx",
74
+ "integrations": {
75
+ "embedJs": {
76
+ "entrypoint": "https://example.test/extension/demo/script",
77
+ "stylesheet": "https://example.test/extension/demo/stylesheet",
78
+ "runner": "demo",
79
+ "pages": [
80
+ {
81
+ "code": "orders-dashboard",
82
+ "menu": "activity_main_menu",
83
+ "parentMenuItemCode": "orders",
84
+ "menuItemOrdering": 100,
85
+ "menuItemTitle": {
86
+ "ru": "Заказы",
87
+ "en": "Orders",
88
+ "es": "Pedidos"
89
+ },
90
+ "pageHelpLink": null
91
+ }
92
+ ]
93
+ }
94
+ }
95
+ }
96
+ }
97
+ ```
98
+
99
+ Если локальный API client не знает DTO для расширенного payload, используйте поддержанный custom/request
100
+ method с обычным JSON-объектом вместо добавления локального DTO, который serializer не умеет обрабатывать.
101
+
64
102
  ## Пример справочника меню
65
103
 
66
104
  Конкретные `placement`, `item code` и `route` нужно брать из host/manifest расширения.
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "embed-ui-v1-endpoint-mcp": "bin/embed-ui-v1-endpoint-mcp.mjs"
6
6
  },
7
7
  "type": "module",
8
- "version": "0.9.24",
8
+ "version": "0.9.25",
9
9
  "description": "Endpoint API for integrations in RetailCRM",
10
10
  "license": "MIT",
11
11
  "author": "RetailDriverLLC <integration@retailcrm.ru>",
@@ -85,7 +85,8 @@
85
85
  "bin",
86
86
  "dist",
87
87
  "docs",
88
- "README.md"
88
+ "README.md",
89
+ "templates"
89
90
  ],
90
91
  "scripts": {
91
92
  "build": "yarn build:docs && yarn build:code && yarn build:mcp && yarn build:meta",
@@ -100,17 +101,17 @@
100
101
  "peerDependencies": {
101
102
  "@omnicajs/vue-remote": "^0.2.24",
102
103
  "@remote-ui/rpc": "^1.4",
103
- "@retailcrm/embed-ui-v1-contexts": "^0.9.24",
104
- "@retailcrm/embed-ui-v1-types": "^0.9.24",
104
+ "@retailcrm/embed-ui-v1-contexts": "^0.9.25",
105
+ "@retailcrm/embed-ui-v1-types": "^0.9.25",
105
106
  "pinia": "^2.2",
106
107
  "vue": "^3.5"
107
108
  },
108
109
  "dependencies": {
109
110
  "@modelcontextprotocol/sdk": "^1.29.0",
110
111
  "@remote-ui/rpc": "^1.4.7",
111
- "@retailcrm/embed-ui-v1-components": "^0.9.24",
112
- "@retailcrm/embed-ui-v1-contexts": "^0.9.24",
113
- "@retailcrm/embed-ui-v1-types": "^0.9.24"
112
+ "@retailcrm/embed-ui-v1-components": "^0.9.25",
113
+ "@retailcrm/embed-ui-v1-contexts": "^0.9.25",
114
+ "@retailcrm/embed-ui-v1-types": "^0.9.25"
114
115
  },
115
116
  "devDependencies": {
116
117
  "@retailcrm/image-preview": "^1.0.2",
@@ -0,0 +1,45 @@
1
+ ---
2
+ name: embed-ui-v1-endpoint-runtime
3
+ description: Use when building, editing, or reviewing RetailCRM JS module endpoint wiring with @retailcrm/embed-ui-v1-endpoint. Covers pages, widgets, runners, targets, menu hierarchy, page publishing payloads, and public Host API boundaries.
4
+ ---
5
+
6
+ # Embed UI v1 Endpoint Runtime
7
+
8
+ Use this skill before changing JS module endpoint wiring, page registration, widget targets, or Host API navigation.
9
+
10
+ ## Reading order
11
+
12
+ 1. Read project `AGENTS.md` if it exists.
13
+ 2. Read `./node_modules/@retailcrm/embed-ui-v1-endpoint/README.md`.
14
+ 3. Read `./node_modules/@retailcrm/embed-ui-v1-endpoint/docs/README.md`.
15
+ 4. Read the relevant endpoint docs:
16
+ - `docs/define-runner.md`
17
+ - `docs/define-page-runner.md`
18
+ - `docs/define-widget-runner.md`
19
+ - `docs/page-routes.md`
20
+ - `docs/menu-placements.md`
21
+ - `docs/targets.md`
22
+ 5. If MCP resources are available, read `embed-ui-v1-endpoint://targets` and then the target profile for the target being changed.
23
+ 6. If MCP is not available, use `./node_modules/@retailcrm/embed-ui-v1-endpoint/docs/targets/*.yml` as fallback.
24
+
25
+ ## Workflow
26
+
27
+ 1. Identify whether the task is a page, widget, or shared endpoint runner change.
28
+ 2. For pages, keep the runtime runner map and extension config page descriptors aligned by page `code`.
29
+ 3. For widgets, choose the exact target from endpoint docs or MCP resources before writing UI.
30
+ 4. Use documented public entrypoints such as `@retailcrm/embed-ui-v1-endpoint/remote`; do not import from `dist/*`, source files, or repository-only paths.
31
+ 5. Keep widget inline UI compact; use component package widget composition guidance for UI mounted into CRM targets.
32
+
33
+ ## Page and menu checks
34
+
35
+ - Published page descriptors must be objects, not string page codes.
36
+ - Page descriptors need `code`, `menu`, and usually `menuItemTitle`.
37
+ - Use `parentMenuItemCode` and `menuItemOrdering` to model the intended CRM page/menu hierarchy.
38
+ - Breadcrumb-like navigation is derived by CRM from the page/menu hierarchy passed in the extension config; do not hand-roll breadcrumbs in the page unless the product task explicitly asks for local in-page navigation.
39
+ - For updating an already installed module, use the existing integration module edit/update flow and put pages under `integrationModule.integrations.embedJs.pages`.
40
+
41
+ ## Host API checks
42
+
43
+ - Use public Host API for location, query state, and navigation.
44
+ - Do not depend on internal endpoint runtime stores or private channel state.
45
+ - Keep route names, page codes, and extension descriptor values documented in the project.