@retailcrm/embed-ui 0.9.22-alpha.4 → 0.9.22-alpha.5
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/CHANGELOG.md +6 -0
- package/README.md +9 -4
- package/bin/embed-ui.mjs +277 -44
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
3
|
|
|
4
|
+
## [0.9.22-alpha.5](https://github.com/retailcrm/embed-ui/compare/v0.9.22-alpha.4...v0.9.22-alpha.5) (2026-05-19)
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* Embed UI init flow improved ([50357d6](https://github.com/retailcrm/embed-ui/commit/50357d647d5288c9794e46ef91a36d2da6620101))
|
|
9
|
+
* Embed UI init MCP setup completed ([749c87a](https://github.com/retailcrm/embed-ui/commit/749c87a64bab67b6ed72aed7391ccf24403943ff))
|
|
4
10
|
## [0.9.22-alpha.4](https://github.com/retailcrm/embed-ui/compare/v0.9.22-alpha.3...v0.9.22-alpha.4) (2026-05-19)
|
|
5
11
|
|
|
6
12
|
### Bug Fixes
|
package/README.md
CHANGED
|
@@ -38,9 +38,11 @@ npx @retailcrm/embed-ui --help
|
|
|
38
38
|
- добавляет `package.json`, если его еще нет;
|
|
39
39
|
- добавляет зависимости `@retailcrm/embed-ui*`, `vue`, `pinia`, `vue-i18n` и dev-зависимости для Vite, TypeScript и ESLint;
|
|
40
40
|
- создает `tsconfig.json`, `vite.config.ts`, `eslint.config.js`, `env.d.ts`;
|
|
41
|
+
- создает или дополняет `.gitignore` типовыми правилами для `node_modules`, `dist`, `coverage`, `.env` и логов;
|
|
41
42
|
- создает стартовые файлы во frontend-каталоге: endpoint worker, страницу настроек, виджет заказа, i18n JSON-сообщения;
|
|
42
43
|
- добавляет `extensionrc.json` и `scripts/publish-extension.mjs`;
|
|
43
|
-
- добавляет `AGENTS.md` и MCP-настройки пакетов, если они не отключены
|
|
44
|
+
- добавляет `AGENTS.md` и MCP-настройки пакетов, если они не отключены флагами;
|
|
45
|
+
- может инициализировать Git-репозиторий, если включить `--git` или выбрать это действие в интерактивном режиме.
|
|
44
46
|
|
|
45
47
|
```bash
|
|
46
48
|
npx @retailcrm/embed-ui init ./web --package-manager yarn
|
|
@@ -52,13 +54,15 @@ npx @retailcrm/embed-ui init ./web --package-manager yarn
|
|
|
52
54
|
- frontend-каталог — позиционный аргумент команды, а если его нет, то `./src` для нового проекта или `./web`, если `./src` уже существует;
|
|
53
55
|
- версия пакетов — версия запущенного CLI;
|
|
54
56
|
- package manager — определяется по единственному lock-файлу в корне проекта, в интерактивном терминале запрашивается у пользователя, в неинтерактивном режиме используется `npm`;
|
|
57
|
+
- интерактивный выбор package manager показывает только найденные в `PATH` варианты, а явно выбранный недоступный manager останавливает `init` до записи файлов;
|
|
55
58
|
- пакеты — `@retailcrm/embed-ui`, `v1-components`, `v1-contexts`, `v1-types` и `v1-endpoint`;
|
|
56
59
|
- установка зависимостей — запускается автоматически после изменения `package.json`;
|
|
57
60
|
- шаблон — создается стартовая конфигурация для страницы настроек и виджета `order/card:common.after`;
|
|
58
61
|
- каталоги — создаются `endpoint`, `pages`, `widgets`, `shared`, `i18n` и `i18n/locales` внутри frontend-каталога;
|
|
59
62
|
- `AGENTS.md` — создается или дополняется инструкциями корневого пакета и выбранных пакетов;
|
|
60
63
|
- MCP — добавляется `.mcp.json` для `v1-endpoint`;
|
|
61
|
-
- client configs для Cursor, Junie и VS Code — не создаются без `--mcp-client-configs`;
|
|
64
|
+
- client configs для Codex CLI, Cursor, Junie и VS Code — не создаются без `--mcp-client-configs`;
|
|
65
|
+
- Git — в неинтерактивном режиме не инициализируется без `--git`, а в интерактивном режиме предлагается, если корень проекта не является Git-репозиторием;
|
|
62
66
|
- существующие файлы не перезаписываются, а зависимости с потенциальным конфликтом не заменяются без явных force-флагов.
|
|
63
67
|
|
|
64
68
|
По умолчанию команда использует текущий рабочий каталог как корень проекта. Его можно задать явно:
|
|
@@ -85,7 +89,8 @@ npx @retailcrm/embed-ui init ./web --cwd ./my-project --package-manager npm
|
|
|
85
89
|
- `--no-template` — не создавать стартовые Vue-файлы и `extensionrc.json`.
|
|
86
90
|
- `--no-agents` — не создавать и не дополнять `AGENTS.md`.
|
|
87
91
|
- `--no-mcp` — не добавлять MCP-настройки пакетов.
|
|
88
|
-
- `--mcp-client-configs cursor,junie,vscode` — дополнительно создать
|
|
92
|
+
- `--mcp-client-configs codex,cursor,junie,vscode` — дополнительно подключить или создать конфиги поддерживаемых AI-клиентов.
|
|
93
|
+
- `--git` — выполнить `git init` в корне проекта, если каталог еще не является Git-репозиторием.
|
|
89
94
|
|
|
90
95
|
`--force` включает силовой режим для управляемых частей, но учитывает флаги `--no-*`. Например, можно обновить только
|
|
91
96
|
агентские инструкции и MCP-настройки без перегенерации стартовых файлов:
|
|
@@ -111,7 +116,7 @@ npx @retailcrm/embed-ui init ./web --force --no-install --no-template
|
|
|
111
116
|
Чтобы добавить только поддерживаемые project-level конфиги, укажите:
|
|
112
117
|
|
|
113
118
|
```bash
|
|
114
|
-
npx @retailcrm/embed-ui init ./web --no-install --mcp-client-configs cursor,junie,vscode
|
|
119
|
+
npx @retailcrm/embed-ui init ./web --no-install --mcp-client-configs codex,cursor,junie,vscode
|
|
115
120
|
```
|
|
116
121
|
|
|
117
122
|
Повторный запуск без `--force` не дублирует управляемые секции и записи MCP. При `--force` обновляются только записи
|
package/bin/embed-ui.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createInterface } from "node:readline/promises";
|
|
3
|
-
import { execFileSync } from "node:child_process";
|
|
3
|
+
import { execFileSync, spawn } from "node:child_process";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import fs from "node:fs";
|
|
6
6
|
import path from "node:path";
|
|
@@ -34,7 +34,8 @@ Options:
|
|
|
34
34
|
--fix-sections Move init dependencies to expected package.json sections
|
|
35
35
|
--no-agents Do not create or update AGENTS.md in init mode
|
|
36
36
|
--no-mcp Do not add package MCP instructions in init mode
|
|
37
|
-
--mcp-client-configs Comma-separated MCP client configs to create (cursor,junie,vscode)
|
|
37
|
+
--mcp-client-configs Comma-separated MCP client configs to create (codex,cursor,junie,vscode)
|
|
38
|
+
--git Initialize Git repository in init mode when cwd is not a Git work tree
|
|
38
39
|
-h, --help Show this help
|
|
39
40
|
|
|
40
41
|
Examples:
|
|
@@ -114,6 +115,10 @@ const parseInitArgs = (argv) => {
|
|
|
114
115
|
type: "string",
|
|
115
116
|
coerce: parsePackageList,
|
|
116
117
|
describe: "Comma-separated MCP client config ids"
|
|
118
|
+
}).option("git", {
|
|
119
|
+
type: "boolean",
|
|
120
|
+
default: false,
|
|
121
|
+
describe: "Initialize Git repository when cwd is not a Git work tree"
|
|
117
122
|
}).parseSync();
|
|
118
123
|
if (parsed.help || parsed.h) {
|
|
119
124
|
console.log(HELP_TEXT);
|
|
@@ -156,7 +161,8 @@ const parseInitArgs = (argv) => {
|
|
|
156
161
|
agentsOnly: parsed.agentsOnly,
|
|
157
162
|
noMcp: !parsed.mcp,
|
|
158
163
|
forceMcp: parsed.forceMcp,
|
|
159
|
-
mcpClientConfigs: parsed.mcpClientConfigs ?? null
|
|
164
|
+
mcpClientConfigs: parsed.mcpClientConfigs ?? null,
|
|
165
|
+
initGit: parsed.git
|
|
160
166
|
};
|
|
161
167
|
};
|
|
162
168
|
const parseArgs = (argv) => {
|
|
@@ -610,13 +616,7 @@ const setDependency = (packageJson, section, name, range, changes, options = {})
|
|
|
610
616
|
nextRange
|
|
611
617
|
});
|
|
612
618
|
};
|
|
613
|
-
const
|
|
614
|
-
const binPath = path.join(cwd, "node_modules", ".bin", process.platform === "win32" ? `${binName}.cmd` : binName);
|
|
615
|
-
return fs.existsSync(binPath) ? binPath : null;
|
|
616
|
-
};
|
|
617
|
-
const hasLocalPackage = (cwd, packageName) => fs.existsSync(path.join(cwd, "node_modules", packageName, "package.json"));
|
|
618
|
-
const createPackageSpec = (packageName, version) => version && version !== "not used" ? `${packageName}@${version}` : packageName;
|
|
619
|
-
const resolvePackageManagerVersion$1 = (packageManager) => {
|
|
619
|
+
const resolvePackageManagerVersion = (packageManager) => {
|
|
620
620
|
try {
|
|
621
621
|
return execFileSync(packageManager, ["--version"], {
|
|
622
622
|
encoding: "utf8",
|
|
@@ -626,6 +626,90 @@ const resolvePackageManagerVersion$1 = (packageManager) => {
|
|
|
626
626
|
return null;
|
|
627
627
|
}
|
|
628
628
|
};
|
|
629
|
+
const isPackageManagerAvailable = (packageManager) => resolvePackageManagerVersion(packageManager) !== null;
|
|
630
|
+
const assertPackageManagerAvailable = (packageManager) => {
|
|
631
|
+
if (!isPackageManagerAvailable(packageManager)) {
|
|
632
|
+
throw new Error(
|
|
633
|
+
`Package manager "${packageManager}" was selected, but its binary was not found in PATH. Install ${packageManager} or choose another package manager.`
|
|
634
|
+
);
|
|
635
|
+
}
|
|
636
|
+
};
|
|
637
|
+
const SPINNER_FRAMES = ["-", "\\", "|", "/"];
|
|
638
|
+
class CommandError extends Error {
|
|
639
|
+
stdout;
|
|
640
|
+
stderr;
|
|
641
|
+
constructor(command, exitCode, stdout, stderr) {
|
|
642
|
+
super(`Command failed${exitCode === null ? "" : ` with exit code ${exitCode}`}: ${command}`);
|
|
643
|
+
this.stdout = stdout;
|
|
644
|
+
this.stderr = stderr;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
const createSpinner = (message) => {
|
|
648
|
+
if (!process.stderr.isTTY) {
|
|
649
|
+
return () => void 0;
|
|
650
|
+
}
|
|
651
|
+
let frameIndex = 0;
|
|
652
|
+
process.stderr.write(`${SPINNER_FRAMES[frameIndex]} ${message}`);
|
|
653
|
+
const timer = setInterval(() => {
|
|
654
|
+
frameIndex = (frameIndex + 1) % SPINNER_FRAMES.length;
|
|
655
|
+
process.stderr.write(`\r${SPINNER_FRAMES[frameIndex]} ${message}`);
|
|
656
|
+
}, 100);
|
|
657
|
+
return () => {
|
|
658
|
+
clearInterval(timer);
|
|
659
|
+
};
|
|
660
|
+
};
|
|
661
|
+
const finishSpinner = (message, status) => {
|
|
662
|
+
if (process.stderr.isTTY) {
|
|
663
|
+
process.stderr.write(`\r${status} ${message}
|
|
664
|
+
`);
|
|
665
|
+
}
|
|
666
|
+
};
|
|
667
|
+
const runCommandWithTerminalStatus = async (command, args, options, message) => {
|
|
668
|
+
const stopSpinner = createSpinner(message);
|
|
669
|
+
const displayCommand = [command, ...args].join(" ");
|
|
670
|
+
let stdoutResult = "";
|
|
671
|
+
let stderrResult = "";
|
|
672
|
+
try {
|
|
673
|
+
await new Promise((resolve, reject) => {
|
|
674
|
+
const child = spawn(command, args, {
|
|
675
|
+
...options,
|
|
676
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
677
|
+
});
|
|
678
|
+
const stdout = [];
|
|
679
|
+
const stderr = [];
|
|
680
|
+
child.stdout.on("data", (chunk) => stdout.push(chunk));
|
|
681
|
+
child.stderr.on("data", (chunk) => stderr.push(chunk));
|
|
682
|
+
child.on("error", reject);
|
|
683
|
+
child.on("close", (exitCode) => {
|
|
684
|
+
const stdoutBuffer = Buffer.concat(stdout);
|
|
685
|
+
const stderrBuffer = Buffer.concat(stderr);
|
|
686
|
+
stdoutResult = stdoutBuffer.toString("utf8");
|
|
687
|
+
stderrResult = stderrBuffer.toString("utf8");
|
|
688
|
+
if (exitCode === 0) {
|
|
689
|
+
resolve();
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
reject(new CommandError(displayCommand, exitCode, stdoutBuffer, stderrBuffer));
|
|
693
|
+
});
|
|
694
|
+
});
|
|
695
|
+
finishSpinner(message, "OK");
|
|
696
|
+
return {
|
|
697
|
+
stdout: stdoutResult,
|
|
698
|
+
stderr: stderrResult
|
|
699
|
+
};
|
|
700
|
+
} catch (error) {
|
|
701
|
+
finishSpinner(message, "FAIL");
|
|
702
|
+
throw error;
|
|
703
|
+
} finally {
|
|
704
|
+
stopSpinner();
|
|
705
|
+
}
|
|
706
|
+
};
|
|
707
|
+
const resolveLocalBinPath = (cwd, binName) => {
|
|
708
|
+
const binPath = path.join(cwd, "node_modules", ".bin", process.platform === "win32" ? `${binName}.cmd` : binName);
|
|
709
|
+
return fs.existsSync(binPath) ? binPath : null;
|
|
710
|
+
};
|
|
711
|
+
const hasLocalPackage = (cwd, packageName) => fs.existsSync(path.join(cwd, "node_modules", packageName, "package.json"));
|
|
712
|
+
const createPackageSpec = (packageName, version) => version && version !== "not used" ? `${packageName}@${version}` : packageName;
|
|
629
713
|
const resolveMajorVersion = (version) => {
|
|
630
714
|
const major = version?.match(/^\d+/u)?.[0];
|
|
631
715
|
return major ? Number(major) : null;
|
|
@@ -677,7 +761,7 @@ const resolveDownloadCommand = (packageName, binName, packageManager, args, pack
|
|
|
677
761
|
source: "transient"
|
|
678
762
|
};
|
|
679
763
|
};
|
|
680
|
-
const resolvePackageHookCommand = (cwd, packageName, binName, packageManager, args, packageVersion = null, versionResolver = resolvePackageManagerVersion
|
|
764
|
+
const resolvePackageHookCommand = (cwd, packageName, binName, packageManager, args, packageVersion = null, versionResolver = resolvePackageManagerVersion) => {
|
|
681
765
|
const localBinPath = resolveLocalBinPath(cwd, binName);
|
|
682
766
|
if (localBinPath) {
|
|
683
767
|
return {
|
|
@@ -709,7 +793,15 @@ const getExecErrorMessage = (error) => {
|
|
|
709
793
|
}
|
|
710
794
|
return String(error);
|
|
711
795
|
};
|
|
712
|
-
const
|
|
796
|
+
const appendMcpHookNotices = (output, changes) => {
|
|
797
|
+
for (const line of output.split(/\r?\n/u)) {
|
|
798
|
+
const trimmedLine = line.trim();
|
|
799
|
+
if (trimmedLine.startsWith("MCP: ")) {
|
|
800
|
+
changes.mcp.push(trimmedLine.slice("MCP: ".length));
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
};
|
|
804
|
+
const runPackageHookCommand = async (cwd, packageName, binName, packageManager, args, failureMode, options, changes) => {
|
|
713
805
|
const command = resolvePackageHookCommand(
|
|
714
806
|
cwd,
|
|
715
807
|
packageName,
|
|
@@ -723,10 +815,13 @@ const runPackageHookCommand = (cwd, packageName, binName, packageManager, args,
|
|
|
723
815
|
return;
|
|
724
816
|
}
|
|
725
817
|
try {
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
818
|
+
const result = await runCommandWithTerminalStatus(
|
|
819
|
+
command.command,
|
|
820
|
+
command.args,
|
|
821
|
+
{ cwd },
|
|
822
|
+
`Running ${packageName} ${args[0]}`
|
|
823
|
+
);
|
|
824
|
+
appendMcpHookNotices(result.stdout, changes);
|
|
730
825
|
} catch (error) {
|
|
731
826
|
if (command.source === "transient" && failureMode === "advisory") {
|
|
732
827
|
changes.warnings.push(`Package hook ${command.display} was skipped: ${getExecErrorMessage(error)}`);
|
|
@@ -771,12 +866,12 @@ const updateRootAgents = (cwd, options, changes) => {
|
|
|
771
866
|
}
|
|
772
867
|
changes.agents.push(`update ${agentsPath}`);
|
|
773
868
|
};
|
|
774
|
-
const runInitAgentsHook = (packageName, binName, cwd, packageManager, failureMode, options, changes) => {
|
|
869
|
+
const runInitAgentsHook = async (packageName, binName, cwd, packageManager, failureMode, options, changes) => {
|
|
775
870
|
const args = ["init-agents", cwd];
|
|
776
871
|
if (options.force || options.forceAgents) {
|
|
777
872
|
args.push("--force");
|
|
778
873
|
}
|
|
779
|
-
runPackageHookCommand(
|
|
874
|
+
await runPackageHookCommand(
|
|
780
875
|
cwd,
|
|
781
876
|
packageName,
|
|
782
877
|
binName,
|
|
@@ -787,7 +882,7 @@ const runInitAgentsHook = (packageName, binName, cwd, packageManager, failureMod
|
|
|
787
882
|
changes
|
|
788
883
|
);
|
|
789
884
|
};
|
|
790
|
-
const applyInitAgents = (cwd, selectedPackages, packageManager, options, changes) => {
|
|
885
|
+
const applyInitAgents = async (cwd, selectedPackages, packageManager, options, changes) => {
|
|
791
886
|
if (options.noAgents) {
|
|
792
887
|
return;
|
|
793
888
|
}
|
|
@@ -801,7 +896,7 @@ const applyInitAgents = (cwd, selectedPackages, packageManager, options, changes
|
|
|
801
896
|
changes.warnings.push(`Skipping ${selectedPackage.id} ${hook.command} because it currently includes MCP instructions`);
|
|
802
897
|
continue;
|
|
803
898
|
}
|
|
804
|
-
runInitAgentsHook(
|
|
899
|
+
await runInitAgentsHook(
|
|
805
900
|
selectedPackage.name,
|
|
806
901
|
hook.binName,
|
|
807
902
|
cwd,
|
|
@@ -813,7 +908,7 @@ const applyInitAgents = (cwd, selectedPackages, packageManager, options, changes
|
|
|
813
908
|
}
|
|
814
909
|
}
|
|
815
910
|
};
|
|
816
|
-
const applyInitPackageConfigHooks = (cwd, selectedPackages, packageManager, options, changes) => {
|
|
911
|
+
const applyInitPackageConfigHooks = async (cwd, selectedPackages, packageManager, options, changes) => {
|
|
817
912
|
for (const selectedPackage of selectedPackages) {
|
|
818
913
|
for (const hook of selectedPackage.hooks ?? []) {
|
|
819
914
|
if (hook.type !== "config") {
|
|
@@ -829,7 +924,7 @@ const applyInitPackageConfigHooks = (cwd, selectedPackages, packageManager, opti
|
|
|
829
924
|
if (hook.requiresMcp && options.mcpClientConfigs?.length) {
|
|
830
925
|
args.push("--mcp-client-configs", options.mcpClientConfigs.join(","));
|
|
831
926
|
}
|
|
832
|
-
runPackageHookCommand(
|
|
927
|
+
await runPackageHookCommand(
|
|
833
928
|
cwd,
|
|
834
929
|
selectedPackage.name,
|
|
835
930
|
hook.binName,
|
|
@@ -912,6 +1007,35 @@ const describePathState = (cwd, relativePath) => {
|
|
|
912
1007
|
}
|
|
913
1008
|
return `${relativePath}: found`;
|
|
914
1009
|
};
|
|
1010
|
+
const isGitWorkTree$2 = (cwd) => {
|
|
1011
|
+
try {
|
|
1012
|
+
execFileSync("git", ["rev-parse", "--is-inside-work-tree"], {
|
|
1013
|
+
cwd,
|
|
1014
|
+
encoding: "utf8",
|
|
1015
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
1016
|
+
});
|
|
1017
|
+
return true;
|
|
1018
|
+
} catch {
|
|
1019
|
+
return false;
|
|
1020
|
+
}
|
|
1021
|
+
};
|
|
1022
|
+
const analyzeGitDirectory = (cwd, options, changes) => {
|
|
1023
|
+
const gitPath = path.join(cwd, ".git");
|
|
1024
|
+
if (!fs.existsSync(gitPath)) {
|
|
1025
|
+
if (options.initGit) {
|
|
1026
|
+
changes.preflight.push("git: missing; git init enabled");
|
|
1027
|
+
}
|
|
1028
|
+
return;
|
|
1029
|
+
}
|
|
1030
|
+
if (isGitWorkTree$2(cwd)) {
|
|
1031
|
+
return;
|
|
1032
|
+
}
|
|
1033
|
+
if (options.initGit) {
|
|
1034
|
+
changes.preflight.push("git: invalid .git metadata; git init enabled");
|
|
1035
|
+
} else {
|
|
1036
|
+
changes.warnings.push(".git exists, but Git does not recognize this directory as a repository; initialize or repair Git metadata before relying on git context");
|
|
1037
|
+
}
|
|
1038
|
+
};
|
|
915
1039
|
const readExistingPackageJson = (packageJsonPath, changes) => {
|
|
916
1040
|
if (!fs.existsSync(packageJsonPath)) {
|
|
917
1041
|
changes.preflight.push("package.json: missing; it will be created");
|
|
@@ -1082,6 +1206,7 @@ const applyInitPreflight = (cwd, sourceRoot, packageManager, selectedPackages, v
|
|
|
1082
1206
|
if (!options.target && !options.srcDir && fs.existsSync(path.join(cwd, "src")) && path.basename(sourceRoot) === "web") {
|
|
1083
1207
|
changes.warnings.push("src/ already exists; generated frontend source root resolved to web/");
|
|
1084
1208
|
}
|
|
1209
|
+
analyzeGitDirectory(cwd, options, changes);
|
|
1085
1210
|
changes.preflight.push(describePathState(cwd, "src"));
|
|
1086
1211
|
changes.preflight.push(describePathState(cwd, "web"));
|
|
1087
1212
|
if (options.noConfigs) {
|
|
@@ -2202,6 +2327,7 @@ const createInitChanges = () => ({
|
|
|
2202
2327
|
files: [],
|
|
2203
2328
|
agents: [],
|
|
2204
2329
|
mcp: [],
|
|
2330
|
+
git: [],
|
|
2205
2331
|
hooks: [],
|
|
2206
2332
|
install: null,
|
|
2207
2333
|
skipped: [],
|
|
@@ -2252,7 +2378,8 @@ const printInitReport = (cwd, sourceRoot, version, packageManager, changes, opti
|
|
|
2252
2378
|
console.log("");
|
|
2253
2379
|
console.log("files");
|
|
2254
2380
|
for (const filePath of changes.files) {
|
|
2255
|
-
|
|
2381
|
+
const hasAction = /^(create|update)\s/u.test(filePath);
|
|
2382
|
+
console.log(` ${hasAction ? filePath : `create ${filePath}`}`);
|
|
2256
2383
|
}
|
|
2257
2384
|
}
|
|
2258
2385
|
if (changes.agents.length > 0) {
|
|
@@ -2276,6 +2403,13 @@ const printInitReport = (cwd, sourceRoot, version, packageManager, changes, opti
|
|
|
2276
2403
|
console.log(` ${options.dryRun ? "would run" : "ran"} ${hook}`);
|
|
2277
2404
|
}
|
|
2278
2405
|
}
|
|
2406
|
+
if (changes.git.length > 0) {
|
|
2407
|
+
console.log("");
|
|
2408
|
+
console.log("git");
|
|
2409
|
+
for (const gitChange of changes.git) {
|
|
2410
|
+
console.log(` ${gitChange}`);
|
|
2411
|
+
}
|
|
2412
|
+
}
|
|
2279
2413
|
if (changes.install) {
|
|
2280
2414
|
console.log("");
|
|
2281
2415
|
console.log("install");
|
|
@@ -2308,9 +2442,10 @@ const printInitSummary = (cwd, sourceRoot, version, packageManager, changes) =>
|
|
|
2308
2442
|
const summary = [
|
|
2309
2443
|
changes.packageJson.length ? `package.json updated (${changes.packageJson.length} change(s))` : null,
|
|
2310
2444
|
changes.directories.length ? `directories created: ${changes.directories.length}` : null,
|
|
2311
|
-
changes.files.length ? `files
|
|
2445
|
+
changes.files.length ? `files changed: ${changes.files.length}` : null,
|
|
2312
2446
|
changes.agents.length ? "AGENTS.md updated" : null,
|
|
2313
2447
|
changes.mcp.length ? "MCP config updated" : null,
|
|
2448
|
+
changes.git.length ? `git: ${changes.git.join(", ")}` : null,
|
|
2314
2449
|
changes.hooks.length ? `package hooks ran: ${changes.hooks.length}` : null,
|
|
2315
2450
|
changes.install ? `install: ${changes.install}` : null
|
|
2316
2451
|
].filter((item) => typeof item === "string");
|
|
@@ -4372,6 +4507,7 @@ const INIT_ACTION_LABELS = {
|
|
|
4372
4507
|
template: "Создать стартовый шаблон",
|
|
4373
4508
|
agents: "Обновить AGENTS.md",
|
|
4374
4509
|
mcp: "Добавить MCP-настройки",
|
|
4510
|
+
git: "Инициализировать Git",
|
|
4375
4511
|
install: "Запустить установку зависимостей"
|
|
4376
4512
|
};
|
|
4377
4513
|
const INIT_ACTION_DESCRIPTIONS = {
|
|
@@ -4379,8 +4515,20 @@ const INIT_ACTION_DESCRIPTIONS = {
|
|
|
4379
4515
|
template: "Vue-точка входа, страница настроек, виджет заказа, i18n и publish script",
|
|
4380
4516
|
agents: "Общие и пакетные инструкции для AI-агентов",
|
|
4381
4517
|
mcp: ".mcp.json и MCP-инструкции пакетов",
|
|
4518
|
+
git: "git init в каталоге проекта, если Git еще не настроен",
|
|
4382
4519
|
install: "Запуск выбранного package manager после изменения package.json"
|
|
4383
4520
|
};
|
|
4521
|
+
const isGitWorkTree$1 = (cwd) => {
|
|
4522
|
+
try {
|
|
4523
|
+
execFileSync("git", ["rev-parse", "--is-inside-work-tree"], {
|
|
4524
|
+
cwd,
|
|
4525
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
4526
|
+
});
|
|
4527
|
+
return true;
|
|
4528
|
+
} catch {
|
|
4529
|
+
return false;
|
|
4530
|
+
}
|
|
4531
|
+
};
|
|
4384
4532
|
const resolveDefaultSourceRoot = (cwd, options) => {
|
|
4385
4533
|
if (options.srcDir) {
|
|
4386
4534
|
return options.srcDir;
|
|
@@ -4425,6 +4573,9 @@ const resolveAvailableActions = (options) => {
|
|
|
4425
4573
|
if (!options.noMcp) {
|
|
4426
4574
|
actions.push("mcp");
|
|
4427
4575
|
}
|
|
4576
|
+
if (!options.agentsOnly && !isGitWorkTree$1(options.cwd)) {
|
|
4577
|
+
actions.push("git");
|
|
4578
|
+
}
|
|
4428
4579
|
if (!options.agentsOnly && !options.noInstall) {
|
|
4429
4580
|
actions.push("install");
|
|
4430
4581
|
}
|
|
@@ -4436,6 +4587,7 @@ const applyPromptedActions = (options, selectedActions) => {
|
|
|
4436
4587
|
options.noTemplate = options.noTemplate || !selectedActionSet.has("template");
|
|
4437
4588
|
options.noAgents = options.noAgents || !selectedActionSet.has("agents");
|
|
4438
4589
|
options.noMcp = options.noMcp || !selectedActionSet.has("mcp");
|
|
4590
|
+
options.initGit = options.initGit || selectedActionSet.has("git");
|
|
4439
4591
|
options.noInstall = options.noInstall || !selectedActionSet.has("install");
|
|
4440
4592
|
};
|
|
4441
4593
|
const resolvePromptedActions = async (options) => {
|
|
@@ -4458,12 +4610,18 @@ const resolvePromptedPackageManager = async (detectedPackageManager, explicitPac
|
|
|
4458
4610
|
return explicitPackageManager;
|
|
4459
4611
|
}
|
|
4460
4612
|
const defaultPackageManager = detectedPackageManager ?? "npm";
|
|
4613
|
+
const availablePackageManagers = PACKAGE_MANAGERS.filter(isPackageManagerAvailable);
|
|
4614
|
+
if (availablePackageManagers.length === 0) {
|
|
4615
|
+
throw new Error("No supported package manager binary was found in PATH. Install npm, yarn, pnpm, or bun and rerun init.");
|
|
4616
|
+
}
|
|
4461
4617
|
return select({
|
|
4462
4618
|
message: "Package manager",
|
|
4463
|
-
default: defaultPackageManager,
|
|
4619
|
+
default: availablePackageManagers.includes(defaultPackageManager) ? defaultPackageManager : availablePackageManagers[0],
|
|
4464
4620
|
choices: PACKAGE_MANAGERS.map((packageManager) => ({
|
|
4465
4621
|
name: packageManager,
|
|
4466
|
-
value: packageManager
|
|
4622
|
+
value: packageManager,
|
|
4623
|
+
description: isPackageManagerAvailable(packageManager) ? "found in PATH" : "not found in PATH",
|
|
4624
|
+
disabled: isPackageManagerAvailable(packageManager) ? false : "not found in PATH"
|
|
4467
4625
|
}))
|
|
4468
4626
|
});
|
|
4469
4627
|
};
|
|
@@ -4497,6 +4655,49 @@ const resolveInteractiveInitOptions = async (cwd, options, detectedPackageManage
|
|
|
4497
4655
|
);
|
|
4498
4656
|
return nextOptions;
|
|
4499
4657
|
};
|
|
4658
|
+
const GITIGNORE_SECTION_HEADER = "# RetailCRM embed-ui init";
|
|
4659
|
+
const REQUIRED_GITIGNORE_ENTRIES = [
|
|
4660
|
+
"node_modules/",
|
|
4661
|
+
"dist/",
|
|
4662
|
+
"coverage/",
|
|
4663
|
+
".env",
|
|
4664
|
+
".env.*",
|
|
4665
|
+
"!.env.example",
|
|
4666
|
+
"*.log",
|
|
4667
|
+
"npm-debug.log*",
|
|
4668
|
+
"yarn-debug.log*",
|
|
4669
|
+
"yarn-error.log*",
|
|
4670
|
+
"pnpm-debug.log*",
|
|
4671
|
+
".DS_Store"
|
|
4672
|
+
];
|
|
4673
|
+
const normalizeGitignoreLine = (line) => line.trim().replace(/\/$/u, "");
|
|
4674
|
+
const hasGitignoreEntry = (lines, entry) => {
|
|
4675
|
+
const normalizedEntry = normalizeGitignoreLine(entry);
|
|
4676
|
+
return lines.some((line) => normalizeGitignoreLine(line) === normalizedEntry);
|
|
4677
|
+
};
|
|
4678
|
+
const updateGitignore = (cwd, options, changes) => {
|
|
4679
|
+
if (options.agentsOnly) {
|
|
4680
|
+
return;
|
|
4681
|
+
}
|
|
4682
|
+
const gitignorePath = path.join(cwd, ".gitignore");
|
|
4683
|
+
const fileExists = fs.existsSync(gitignorePath);
|
|
4684
|
+
const currentContent = fileExists ? fs.readFileSync(gitignorePath, "utf8") : "";
|
|
4685
|
+
const lines = currentContent.split(/\r?\n/u);
|
|
4686
|
+
const missingEntries = REQUIRED_GITIGNORE_ENTRIES.filter((entry) => !hasGitignoreEntry(lines, entry));
|
|
4687
|
+
if (missingEntries.length === 0) {
|
|
4688
|
+
changes.skipped.push(`${gitignorePath} already contains required init entries`);
|
|
4689
|
+
return;
|
|
4690
|
+
}
|
|
4691
|
+
const section = [
|
|
4692
|
+
GITIGNORE_SECTION_HEADER,
|
|
4693
|
+
...missingEntries
|
|
4694
|
+
].join(DEFAULT_NEWLINE);
|
|
4695
|
+
const nextContent = currentContent.trimEnd() ? `${currentContent.trimEnd()}${DEFAULT_NEWLINE}${DEFAULT_NEWLINE}${section}${DEFAULT_NEWLINE}` : `${section}${DEFAULT_NEWLINE}`;
|
|
4696
|
+
if (!options.dryRun) {
|
|
4697
|
+
fs.writeFileSync(gitignorePath, nextContent, "utf8");
|
|
4698
|
+
}
|
|
4699
|
+
changes.files.push(`${fileExists ? "update" : "create"} ${gitignorePath}`);
|
|
4700
|
+
};
|
|
4500
4701
|
const DEFAULT_INIT_DIRS = ["endpoint", "pages", "widgets", "shared", "i18n"];
|
|
4501
4702
|
const detectPackageManagerByLockfile = (cwd) => {
|
|
4502
4703
|
const knownLockfiles = [
|
|
@@ -4508,16 +4709,6 @@ const detectPackageManagerByLockfile = (cwd) => {
|
|
|
4508
4709
|
const lockfiles = knownLockfiles.filter(({ file }) => fs.existsSync(path.join(cwd, file)));
|
|
4509
4710
|
return lockfiles.length === 1 ? lockfiles[0].packageManager : null;
|
|
4510
4711
|
};
|
|
4511
|
-
const resolvePackageManagerVersion = (packageManager) => {
|
|
4512
|
-
try {
|
|
4513
|
-
return execFileSync(packageManager, ["--version"], {
|
|
4514
|
-
encoding: "utf8",
|
|
4515
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
4516
|
-
}).trim();
|
|
4517
|
-
} catch {
|
|
4518
|
-
return null;
|
|
4519
|
-
}
|
|
4520
|
-
};
|
|
4521
4712
|
const resolvePackageManagerMajorVersion = (packageManager) => {
|
|
4522
4713
|
const major = resolvePackageManagerVersion(packageManager)?.match(/^\d+/u)?.[0];
|
|
4523
4714
|
return major ? Number(major) : null;
|
|
@@ -4565,6 +4756,16 @@ const resolveInitPackages = (tokens, extraTokens) => {
|
|
|
4565
4756
|
}
|
|
4566
4757
|
return packages;
|
|
4567
4758
|
};
|
|
4759
|
+
const hasEnabledPackageHook = (selectedPackages, options) => selectedPackages.some((selectedPackage) => selectedPackage.hooks?.some((hook) => {
|
|
4760
|
+
if (hook.type === "agents") {
|
|
4761
|
+
return !options.noAgents && (!hook.requiresMcp || !options.noMcp);
|
|
4762
|
+
}
|
|
4763
|
+
if (hook.type === "config") {
|
|
4764
|
+
return !options.agentsOnly && (!hook.requiresMcp || !options.noMcp);
|
|
4765
|
+
}
|
|
4766
|
+
return false;
|
|
4767
|
+
}) ?? false);
|
|
4768
|
+
const shouldRequirePackageManagerBinary = (selectedPackages, options) => !options.dryRun && (!options.noInstall || hasEnabledPackageHook(selectedPackages, options));
|
|
4568
4769
|
const resolveInitCwd = (options) => {
|
|
4569
4770
|
const cwd = path.resolve(options.cwd);
|
|
4570
4771
|
if (!fs.existsSync(cwd)) {
|
|
@@ -4588,6 +4789,17 @@ const resolveInitSourceRoot = (cwd, options) => {
|
|
|
4588
4789
|
}
|
|
4589
4790
|
return path.join(cwd, "web");
|
|
4590
4791
|
};
|
|
4792
|
+
const isGitWorkTree = (cwd) => {
|
|
4793
|
+
try {
|
|
4794
|
+
execFileSync("git", ["rev-parse", "--is-inside-work-tree"], {
|
|
4795
|
+
cwd,
|
|
4796
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
4797
|
+
});
|
|
4798
|
+
return true;
|
|
4799
|
+
} catch {
|
|
4800
|
+
return false;
|
|
4801
|
+
}
|
|
4802
|
+
};
|
|
4591
4803
|
const runUpdate = (options) => {
|
|
4592
4804
|
const version = options.version ?? resolveLatestVersion();
|
|
4593
4805
|
const packageJsonPaths = collectPackageJsonPaths(options.target);
|
|
@@ -4754,7 +4966,7 @@ const applyInitTemplate = (cwd, sourceRoot, packageManager, options, changes) =>
|
|
|
4754
4966
|
writeFileIfAllowed(path.join(cwd, "scripts/publish-extension.mjs"), createPublishScript(), options, changes);
|
|
4755
4967
|
writeFileIfAllowed(path.join(cwd, "README.md"), createReadme(cwd, sourceRoot, options, packageManager), options, changes);
|
|
4756
4968
|
};
|
|
4757
|
-
const runInstall = (cwd, packageManager, options, changes, packageJsonChanged) => {
|
|
4969
|
+
const runInstall = async (cwd, packageManager, options, changes, packageJsonChanged) => {
|
|
4758
4970
|
if (options.noInstall || options.agentsOnly) {
|
|
4759
4971
|
return;
|
|
4760
4972
|
}
|
|
@@ -4768,15 +4980,31 @@ const runInstall = (cwd, packageManager, options, changes, packageJsonChanged) =
|
|
|
4768
4980
|
return;
|
|
4769
4981
|
}
|
|
4770
4982
|
try {
|
|
4771
|
-
|
|
4772
|
-
|
|
4773
|
-
|
|
4774
|
-
|
|
4983
|
+
await runCommandWithTerminalStatus(
|
|
4984
|
+
packageManager,
|
|
4985
|
+
args,
|
|
4986
|
+
{ cwd },
|
|
4987
|
+
`Installing dependencies with ${packageManager}`
|
|
4988
|
+
);
|
|
4775
4989
|
} catch (error) {
|
|
4776
4990
|
printExecErrorOutput(error);
|
|
4777
4991
|
throw error;
|
|
4778
4992
|
}
|
|
4779
4993
|
};
|
|
4994
|
+
const applyInitGit = async (cwd, options, changes) => {
|
|
4995
|
+
if (!options.initGit) {
|
|
4996
|
+
return;
|
|
4997
|
+
}
|
|
4998
|
+
if (isGitWorkTree(cwd)) {
|
|
4999
|
+
changes.skipped.push("git init skipped because cwd is already inside a Git work tree");
|
|
5000
|
+
return;
|
|
5001
|
+
}
|
|
5002
|
+
changes.git.push("git init");
|
|
5003
|
+
if (options.dryRun) {
|
|
5004
|
+
return;
|
|
5005
|
+
}
|
|
5006
|
+
await runCommandWithTerminalStatus("git", ["init"], { cwd }, "Initializing Git repository");
|
|
5007
|
+
};
|
|
4780
5008
|
const resolveInstallArgs = (packageManager) => {
|
|
4781
5009
|
if (packageManager === "yarn" && resolvePackageManagerMajorVersion(packageManager) === 1) {
|
|
4782
5010
|
return ["install", "--silent"];
|
|
@@ -4821,17 +5049,22 @@ const runInit = async (options) => {
|
|
|
4821
5049
|
const resolvedOptions = version === "not used" ? interactiveOptions : { ...interactiveOptions, version };
|
|
4822
5050
|
const packageManager = interactiveOptions.agentsOnly ? interactiveOptions.packageManager ?? detectPackageManagerByLockfile(cwd) ?? "npm" : await resolvePackageManager(cwd, interactiveOptions.packageManager);
|
|
4823
5051
|
const changes = createInitChanges();
|
|
5052
|
+
if (shouldRequirePackageManagerBinary(selectedPackages, resolvedOptions)) {
|
|
5053
|
+
assertPackageManagerAvailable(packageManager);
|
|
5054
|
+
}
|
|
4824
5055
|
applyInitPreflight(cwd, sourceRoot, packageManager, selectedPackages, version, resolvedOptions, changes);
|
|
5056
|
+
await applyInitGit(cwd, resolvedOptions, changes);
|
|
4825
5057
|
let packageJsonPath = null;
|
|
4826
5058
|
if (!resolvedOptions.agentsOnly) {
|
|
4827
5059
|
packageJsonPath = applyInitPackageJson(cwd, selectedPackages, version, packageManager, resolvedOptions, changes);
|
|
5060
|
+
updateGitignore(cwd, resolvedOptions, changes);
|
|
4828
5061
|
applyInitDirectories(sourceRoot, resolvedOptions, changes);
|
|
4829
5062
|
applyInitConfigs(cwd, sourceRoot, resolvedOptions, changes);
|
|
4830
5063
|
applyInitTemplate(cwd, sourceRoot, packageManager, resolvedOptions, changes);
|
|
4831
|
-
applyInitPackageConfigHooks(cwd, selectedPackages, packageManager, resolvedOptions, changes);
|
|
4832
5064
|
}
|
|
4833
|
-
|
|
4834
|
-
|
|
5065
|
+
await runInstall(cwd, packageManager, resolvedOptions, changes, Boolean(packageJsonPath && changes.packageJson.length > 0));
|
|
5066
|
+
await applyInitPackageConfigHooks(cwd, selectedPackages, packageManager, resolvedOptions, changes);
|
|
5067
|
+
await applyInitAgents(cwd, selectedPackages, packageManager, resolvedOptions, changes);
|
|
4835
5068
|
printInitReport(cwd, sourceRoot, version, packageManager, changes, resolvedOptions);
|
|
4836
5069
|
};
|
|
4837
5070
|
const main = async (argv = process$2.argv.slice(2)) => {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@retailcrm/embed-ui",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.9.22-alpha.
|
|
4
|
+
"version": "0.9.22-alpha.5",
|
|
5
5
|
"description": "API and components for creating RetailCRM UI extensions",
|
|
6
6
|
"repository": "git@github.com:retailcrm/embed-ui.git",
|
|
7
7
|
"author": "RetailDriverLLC <integration@retailcrm.ru>",
|
|
@@ -54,10 +54,10 @@
|
|
|
54
54
|
"@omnicajs/symfony-router": "^1.0.0",
|
|
55
55
|
"@omnicajs/vue-remote": "^0.2.23",
|
|
56
56
|
"@remote-ui/rpc": "^1.4.5",
|
|
57
|
-
"@retailcrm/embed-ui-v1-components": "^0.9.22-alpha.
|
|
58
|
-
"@retailcrm/embed-ui-v1-contexts": "^0.9.22-alpha.
|
|
59
|
-
"@retailcrm/embed-ui-v1-endpoint": "^0.9.22-alpha.
|
|
60
|
-
"@retailcrm/embed-ui-v1-types": "^0.9.22-alpha.
|
|
57
|
+
"@retailcrm/embed-ui-v1-components": "^0.9.22-alpha.5",
|
|
58
|
+
"@retailcrm/embed-ui-v1-contexts": "^0.9.22-alpha.5",
|
|
59
|
+
"@retailcrm/embed-ui-v1-endpoint": "^0.9.22-alpha.5",
|
|
60
|
+
"@retailcrm/embed-ui-v1-types": "^0.9.22-alpha.5",
|
|
61
61
|
"yargs": "^17.7.2"
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"@modulify/git-toolkit": "^0.0.2",
|
|
67
67
|
"@modulify/pkg": "^1.0.1",
|
|
68
68
|
"@omnicajs/eslint-plugin-dependencies": "^0.0.2",
|
|
69
|
-
"@retailcrm/embed-ui-v1-testing": "^0.9.22-alpha.
|
|
69
|
+
"@retailcrm/embed-ui-v1-testing": "^0.9.22-alpha.5",
|
|
70
70
|
"@types/git-semver-tags": "^7.0.0",
|
|
71
71
|
"@types/node": "^22.19.2",
|
|
72
72
|
"@types/semver": "^7.7.1",
|