@yoyoboot/yoyo-admin-cli 10.5.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/dist/bin.d.ts +9 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +10 -0
- package/dist/catalogs/capability.d.ts +54 -0
- package/dist/catalogs/capability.d.ts.map +1 -0
- package/dist/catalogs/capability.js +129 -0
- package/dist/catalogs/crud.d.ts +64 -0
- package/dist/catalogs/crud.d.ts.map +1 -0
- package/dist/catalogs/crud.js +83 -0
- package/dist/catalogs/source.d.ts +16 -0
- package/dist/catalogs/source.d.ts.map +1 -0
- package/dist/catalogs/source.js +8 -0
- package/dist/catalogs/standard-crud.d.ts +51 -0
- package/dist/catalogs/standard-crud.d.ts.map +1 -0
- package/dist/catalogs/standard-crud.js +100 -0
- package/dist/cli.d.ts +20 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +86 -0
- package/dist/commands/capabilities.d.ts +78 -0
- package/dist/commands/capabilities.d.ts.map +1 -0
- package/dist/commands/capabilities.js +72 -0
- package/dist/commands/contracts.d.ts +16 -0
- package/dist/commands/contracts.d.ts.map +1 -0
- package/dist/commands/contracts.js +83 -0
- package/dist/commands/create-frontend-module.d.ts +8 -0
- package/dist/commands/create-frontend-module.d.ts.map +1 -0
- package/dist/commands/create-frontend-module.js +90 -0
- package/dist/commands/create-package.d.ts +17 -0
- package/dist/commands/create-package.d.ts.map +1 -0
- package/dist/commands/create-package.js +75 -0
- package/dist/commands/create-registry.d.ts +32 -0
- package/dist/commands/create-registry.d.ts.map +1 -0
- package/dist/commands/create-registry.js +20 -0
- package/dist/commands/create.d.ts +23 -0
- package/dist/commands/create.d.ts.map +1 -0
- package/dist/commands/create.js +111 -0
- package/dist/commands/doctor.d.ts +17 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +150 -0
- package/dist/commands/inspect.d.ts +139 -0
- package/dist/commands/inspect.d.ts.map +1 -0
- package/dist/commands/inspect.js +379 -0
- package/dist/generators/frontend-module.d.ts +81 -0
- package/dist/generators/frontend-module.d.ts.map +1 -0
- package/dist/generators/frontend-module.js +358 -0
- package/dist/generators/package.d.ts +46 -0
- package/dist/generators/package.d.ts.map +1 -0
- package/dist/generators/package.js +230 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/kernel/arguments.d.ts +44 -0
- package/dist/kernel/arguments.d.ts.map +1 -0
- package/dist/kernel/arguments.js +127 -0
- package/dist/kernel/command.d.ts +71 -0
- package/dist/kernel/command.d.ts.map +1 -0
- package/dist/kernel/command.js +120 -0
- package/dist/kernel/fs-plan.d.ts +50 -0
- package/dist/kernel/fs-plan.d.ts.map +1 -0
- package/dist/kernel/fs-plan.js +82 -0
- package/dist/kernel/help.d.ts +54 -0
- package/dist/kernel/help.d.ts.map +1 -0
- package/dist/kernel/help.js +109 -0
- package/dist/kernel/lockfile.d.ts +73 -0
- package/dist/kernel/lockfile.d.ts.map +1 -0
- package/dist/kernel/lockfile.js +117 -0
- package/dist/kernel/manifests-wiring.d.ts +44 -0
- package/dist/kernel/manifests-wiring.d.ts.map +1 -0
- package/dist/kernel/manifests-wiring.js +165 -0
- package/dist/kernel/plan-runner.d.ts +81 -0
- package/dist/kernel/plan-runner.d.ts.map +1 -0
- package/dist/kernel/plan-runner.js +176 -0
- package/dist/kernel/process-runner.d.ts +25 -0
- package/dist/kernel/process-runner.d.ts.map +1 -0
- package/dist/kernel/process-runner.js +29 -0
- package/dist/kernel/render.d.ts +20 -0
- package/dist/kernel/render.d.ts.map +1 -0
- package/dist/kernel/render.js +63 -0
- package/dist/kernel/result.d.ts +66 -0
- package/dist/kernel/result.d.ts.map +1 -0
- package/dist/kernel/result.js +36 -0
- package/dist/kernel/workspace.d.ts +31 -0
- package/dist/kernel/workspace.d.ts.map +1 -0
- package/dist/kernel/workspace.js +66 -0
- package/package.json +31 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `yoyo-admin` CLI 入口。
|
|
3
|
+
*
|
|
4
|
+
* 退出码约定:
|
|
5
|
+
* 0 = 成功(含 `--help`)
|
|
6
|
+
* 2 = 预期失败(用户输入或仓库状态不允许执行)
|
|
7
|
+
* 1 = 未预期失败(程序缺陷)
|
|
8
|
+
*/
|
|
9
|
+
import { dispatch, YoyoAdminCliRegistry } from './kernel/command.js';
|
|
10
|
+
import { isHelpPayload } from './kernel/help.js';
|
|
11
|
+
import { renderCapabilities, renderFsPlan, renderJson, renderResult } from './kernel/render.js';
|
|
12
|
+
import { failure, finding, hasError, YoyoAdminCliUsageError } from './kernel/result.js';
|
|
13
|
+
import { isFsPlan } from './kernel/fs-plan.js';
|
|
14
|
+
import { createCapabilitiesCommand } from './commands/capabilities.js';
|
|
15
|
+
import { createCommand } from './commands/create.js';
|
|
16
|
+
import { doctorCommand } from './commands/doctor.js';
|
|
17
|
+
import { inspectCommand, isInspectPayload, renderInspectPayload } from './commands/inspect.js';
|
|
18
|
+
import { createContractsCommand, isContractsStatusPayload, renderContractsStatus } from './commands/contracts.js';
|
|
19
|
+
export const EXIT_OK = 0;
|
|
20
|
+
export const EXIT_UNEXPECTED = 1;
|
|
21
|
+
export const EXIT_USAGE = 2;
|
|
22
|
+
export function createRegistry() {
|
|
23
|
+
const registry = new YoyoAdminCliRegistry();
|
|
24
|
+
registry.register(doctorCommand, createCommand, inspectCommand, createContractsCommand());
|
|
25
|
+
// capabilities 需要读取注册表自身,因此在其余命令注册完成后创建。
|
|
26
|
+
registry.register(createCapabilitiesCommand(registry));
|
|
27
|
+
return registry;
|
|
28
|
+
}
|
|
29
|
+
export function runCli(argv, options = {}) {
|
|
30
|
+
const write = options.write ?? ((text) => process.stdout.write(text));
|
|
31
|
+
// 失败路径拿不到解析结果;`--json` / `--dry-run` 是布尔 flag,永不接受取值,
|
|
32
|
+
// 因此直接用 argv 判定与解析结果一致。
|
|
33
|
+
const fallback = { json: argv.includes('--json'), dryRun: argv.includes('--dry-run') };
|
|
34
|
+
const registry = createRegistry();
|
|
35
|
+
try {
|
|
36
|
+
const outcome = dispatch(registry, argv, options.startDirectory === undefined ? {} : { startDirectory: options.startDirectory });
|
|
37
|
+
emit(outcome.result, { json: outcome.json, dryRun: outcome.dryRun, write });
|
|
38
|
+
return hasError(outcome.result) ? EXIT_USAGE : EXIT_OK;
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
if (error instanceof YoyoAdminCliUsageError) {
|
|
42
|
+
const result = failure('cli', [finding(error.code, 'error', error.message)]);
|
|
43
|
+
emit(result, { ...fallback, write });
|
|
44
|
+
return EXIT_USAGE;
|
|
45
|
+
}
|
|
46
|
+
const result = failure('cli', [
|
|
47
|
+
finding('unexpected-error', 'error', 'CLI 遇到未预期错误。', error instanceof Error ? error.stack ?? error.message : String(error)),
|
|
48
|
+
]);
|
|
49
|
+
emit(result, { ...fallback, write });
|
|
50
|
+
return EXIT_UNEXPECTED;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function emit(result, options) {
|
|
54
|
+
// help 永远输出人类可读文本(即使带 --json)——它本身就是给人看的投影。
|
|
55
|
+
if (isHelpPayload(result.data)) {
|
|
56
|
+
options.write(result.data.text);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (options.json) {
|
|
60
|
+
options.write(renderJson(result));
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const plan = result.data !== undefined && isFsPlan(result.data) ? result.data : undefined;
|
|
64
|
+
const inspect = result.data !== undefined && isInspectPayload(result.data) ? result.data : undefined;
|
|
65
|
+
const contracts = isContractsStatusPayload(result.data) ? result.data : undefined;
|
|
66
|
+
// 有专用渲染器的载荷不再重复 dump JSON。
|
|
67
|
+
options.write(renderResult(result, { omitData: plan !== undefined || inspect !== undefined || contracts !== undefined }));
|
|
68
|
+
if (contracts !== undefined) {
|
|
69
|
+
options.write(renderContractsStatus(contracts));
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (plan !== undefined) {
|
|
73
|
+
options.write(renderFsPlan(plan, { dryRun: options.dryRun }));
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (inspect !== undefined) {
|
|
77
|
+
options.write(renderInspectPayload(inspect));
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (result.command === 'capabilities') {
|
|
81
|
+
const capabilities = result.data;
|
|
82
|
+
if (capabilities?.recipes) {
|
|
83
|
+
options.write(renderCapabilities(capabilities.recipes));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `yoyo-admin capabilities`
|
|
3
|
+
*
|
|
4
|
+
* 实时机器可读的能力清单。
|
|
5
|
+
*
|
|
6
|
+
* 这是 Agent 选择 recipe 的**唯一事实源**:Skill 先调用本命令,
|
|
7
|
+
* 再决定用哪个 recipe,而不是从文档里记忆未来能力。
|
|
8
|
+
*
|
|
9
|
+
* create 的 recipes 直接从 create-registry 投影(含 kinds、必填参数与 flags),
|
|
10
|
+
* 与 dispatch/help 共用同一注册表,不存在多处漂移。
|
|
11
|
+
*/
|
|
12
|
+
import { type YoyoAdminCliRegistry } from '../kernel/command.js';
|
|
13
|
+
export declare const CAPABILITIES_SCHEMA_VERSION = "1.3.0";
|
|
14
|
+
export interface CapabilitiesPayload {
|
|
15
|
+
readonly schemaVersion: string;
|
|
16
|
+
readonly cli: {
|
|
17
|
+
readonly name: string;
|
|
18
|
+
readonly invocation: string;
|
|
19
|
+
readonly globalFlags: readonly {
|
|
20
|
+
readonly flag: string;
|
|
21
|
+
readonly description: string;
|
|
22
|
+
}[];
|
|
23
|
+
};
|
|
24
|
+
readonly commands: readonly {
|
|
25
|
+
readonly name: string;
|
|
26
|
+
readonly summary: string;
|
|
27
|
+
readonly usage: string;
|
|
28
|
+
readonly authoring: boolean;
|
|
29
|
+
readonly capability?: string;
|
|
30
|
+
readonly booleanFlags?: readonly string[];
|
|
31
|
+
readonly arguments: readonly {
|
|
32
|
+
readonly name: string;
|
|
33
|
+
readonly required: boolean;
|
|
34
|
+
readonly description: string;
|
|
35
|
+
}[];
|
|
36
|
+
readonly options: readonly {
|
|
37
|
+
readonly flag: string;
|
|
38
|
+
readonly description: string;
|
|
39
|
+
readonly values?: readonly string[];
|
|
40
|
+
readonly default?: string;
|
|
41
|
+
}[];
|
|
42
|
+
}[];
|
|
43
|
+
readonly create: {
|
|
44
|
+
readonly recipes: readonly {
|
|
45
|
+
readonly name: string;
|
|
46
|
+
readonly kinds: readonly string[];
|
|
47
|
+
readonly summary: string;
|
|
48
|
+
readonly usage: string;
|
|
49
|
+
readonly options: readonly {
|
|
50
|
+
readonly flag: string;
|
|
51
|
+
readonly required: boolean;
|
|
52
|
+
readonly description: string;
|
|
53
|
+
readonly values?: readonly string[];
|
|
54
|
+
}[];
|
|
55
|
+
}[];
|
|
56
|
+
};
|
|
57
|
+
readonly inspect: {
|
|
58
|
+
readonly subjects: readonly {
|
|
59
|
+
readonly name: string;
|
|
60
|
+
readonly summary: string;
|
|
61
|
+
readonly usage: string;
|
|
62
|
+
readonly arguments: readonly {
|
|
63
|
+
readonly name: string;
|
|
64
|
+
readonly required: boolean;
|
|
65
|
+
readonly description: string;
|
|
66
|
+
}[];
|
|
67
|
+
readonly options: readonly {
|
|
68
|
+
readonly flag: string;
|
|
69
|
+
readonly required: boolean;
|
|
70
|
+
readonly description: string;
|
|
71
|
+
readonly values?: readonly string[];
|
|
72
|
+
}[];
|
|
73
|
+
}[];
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
export declare function buildCapabilities(registry: YoyoAdminCliRegistry): CapabilitiesPayload;
|
|
77
|
+
export declare function createCapabilitiesCommand(registry: YoyoAdminCliRegistry): import("../index.js").RegisteredCommand;
|
|
78
|
+
//# sourceMappingURL=capabilities.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capabilities.d.ts","sourceRoot":"","sources":["../../src/commands/capabilities.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `yoyo-admin capabilities`
|
|
3
|
+
*
|
|
4
|
+
* 实时机器可读的能力清单。
|
|
5
|
+
*
|
|
6
|
+
* 这是 Agent 选择 recipe 的**唯一事实源**:Skill 先调用本命令,
|
|
7
|
+
* 再决定用哪个 recipe,而不是从文档里记忆未来能力。
|
|
8
|
+
*
|
|
9
|
+
* create 的 recipes 直接从 create-registry 投影(含 kinds、必填参数与 flags),
|
|
10
|
+
* 与 dispatch/help 共用同一注册表,不存在多处漂移。
|
|
11
|
+
*/
|
|
12
|
+
import { defineCommand } from '../kernel/command.js';
|
|
13
|
+
import { ok } from '../kernel/result.js';
|
|
14
|
+
import { listCreateRecipes } from './create-registry.js';
|
|
15
|
+
import { listInspectSubjects } from './inspect.js';
|
|
16
|
+
export const CAPABILITIES_SCHEMA_VERSION = '1.3.0';
|
|
17
|
+
export function buildCapabilities(registry) {
|
|
18
|
+
return {
|
|
19
|
+
schemaVersion: CAPABILITIES_SCHEMA_VERSION,
|
|
20
|
+
cli: {
|
|
21
|
+
name: 'yoyo-admin',
|
|
22
|
+
invocation: 'yoyo-admin <command> [arguments] [options]',
|
|
23
|
+
globalFlags: [
|
|
24
|
+
{ flag: '--json', description: '以 JSON 输出完整结果信封,供机器读取。' },
|
|
25
|
+
{ flag: '--dry-run', description: '仅展示计划变更,不写入任何文件(仅作者命令)。' },
|
|
26
|
+
{ flag: '--help', description: '展示帮助;`yoyo-admin --help` / `yoyo-admin create package --help`。' },
|
|
27
|
+
{ flag: '--repo', description: '显式指定仓库查找起点;缺省从当前工作目录向上查找。' },
|
|
28
|
+
],
|
|
29
|
+
},
|
|
30
|
+
commands: registry.describeCapabilities().map(command => ({
|
|
31
|
+
name: command.name,
|
|
32
|
+
summary: command.summary,
|
|
33
|
+
usage: command.usage,
|
|
34
|
+
authoring: command.authoring,
|
|
35
|
+
...(command.capability === undefined ? {} : { capability: command.capability }),
|
|
36
|
+
arguments: command.arguments,
|
|
37
|
+
options: command.options,
|
|
38
|
+
...(command.booleanFlags === undefined ? {} : { booleanFlags: command.booleanFlags }),
|
|
39
|
+
})),
|
|
40
|
+
create: {
|
|
41
|
+
recipes: listCreateRecipes().map(recipe => ({
|
|
42
|
+
name: recipe.name,
|
|
43
|
+
kinds: recipe.kinds,
|
|
44
|
+
summary: recipe.summary,
|
|
45
|
+
usage: recipe.usage,
|
|
46
|
+
options: recipe.options,
|
|
47
|
+
})),
|
|
48
|
+
},
|
|
49
|
+
inspect: {
|
|
50
|
+
subjects: listInspectSubjects().map(subject => ({
|
|
51
|
+
name: subject.name,
|
|
52
|
+
summary: subject.summary,
|
|
53
|
+
usage: subject.usage,
|
|
54
|
+
arguments: subject.arguments,
|
|
55
|
+
options: subject.options,
|
|
56
|
+
})),
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
export function createCapabilitiesCommand(registry) {
|
|
61
|
+
return defineCommand({
|
|
62
|
+
descriptor: {
|
|
63
|
+
name: 'capabilities',
|
|
64
|
+
summary: '列出 CLI 当前真实支持的命令与 recipe;Agent 应据此选择工作流。',
|
|
65
|
+
usage: 'yoyo-admin capabilities [--repo <path>] [--json]',
|
|
66
|
+
arguments: [],
|
|
67
|
+
options: [],
|
|
68
|
+
authoring: false,
|
|
69
|
+
},
|
|
70
|
+
run: () => ok('capabilities', [], buildCapabilities(registry)),
|
|
71
|
+
});
|
|
72
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type ProcessRunner } from '../kernel/process-runner.js';
|
|
2
|
+
export type ContractStatus = 'current' | 'stale' | 'unavailable' | 'error';
|
|
3
|
+
export interface ContractCheck {
|
|
4
|
+
readonly status: ContractStatus;
|
|
5
|
+
readonly owner: string;
|
|
6
|
+
readonly command: string;
|
|
7
|
+
readonly message: string;
|
|
8
|
+
}
|
|
9
|
+
export interface ContractsStatusPayload {
|
|
10
|
+
readonly kind: 'contracts-status';
|
|
11
|
+
readonly checks: Readonly<Record<'capability' | 'actionContracts' | 'apiGenerated' | 'validationLinkage' | 'liveOpenApi', ContractCheck>>;
|
|
12
|
+
}
|
|
13
|
+
export declare function createContractsCommand(runner?: ProcessRunner, environment?: NodeJS.ProcessEnv): import("../index.js").RegisteredCommand;
|
|
14
|
+
export declare function isContractsStatusPayload(value: unknown): value is ContractsStatusPayload;
|
|
15
|
+
export declare function renderContractsStatus(data: ContractsStatusPayload): string;
|
|
16
|
+
//# sourceMappingURL=contracts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contracts.d.ts","sourceRoot":"","sources":["../../src/commands/contracts.ts"],"names":[],"mappings":"AAEA,OAAO,EAAc,KAAK,aAAa,EAAuB,MAAM,6BAA6B,CAAA;AAGjG,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,OAAO,GAAG,aAAa,GAAG,OAAO,CAAA;AAC1E,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAA;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACzB;AACD,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAA;IACjC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,YAAY,GAAG,iBAAiB,GAAG,cAAc,GAAG,mBAAmB,GAAG,aAAa,EAAE,aAAa,CAAC,CAAC,CAAA;CAC1I;AAID,wBAAgB,sBAAsB,CACpC,MAAM,GAAE,aAA0B,EAClC,WAAW,GAAE,MAAM,CAAC,UAAwB,2CAqE7C;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,sBAAsB,CAExF;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,sBAAsB,GAAG,MAAM,CAI1E"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { defineCommand, requireWorkspace } from '../kernel/command.js';
|
|
3
|
+
import { runProcess } from '../kernel/process-runner.js';
|
|
4
|
+
import { failure, finding, ok, warn, YoyoAdminCliUsageError } from '../kernel/result.js';
|
|
5
|
+
const liveHint = '启动 Web.Host 并设置 YOYO_ADMIN_OPENAPI_URL,或使用 Run-FullStack.ps1 -RefreshApiClient。';
|
|
6
|
+
export function createContractsCommand(runner = runProcess, environment = process.env) {
|
|
7
|
+
return defineCommand({
|
|
8
|
+
descriptor: {
|
|
9
|
+
name: 'contracts',
|
|
10
|
+
summary: '只读契约同步诊断;--check 要求全部 current,否则 exit 2。',
|
|
11
|
+
usage: 'yoyo-admin contracts status [--check] [--json]',
|
|
12
|
+
arguments: [{ name: 'status', required: true, description: '只读检查五类契约同步状态。' }],
|
|
13
|
+
options: [],
|
|
14
|
+
booleanFlags: ['--check'],
|
|
15
|
+
authoring: false,
|
|
16
|
+
},
|
|
17
|
+
run: context => {
|
|
18
|
+
const { positionals } = context.parsed;
|
|
19
|
+
if (positionals.length !== 1 || positionals[0] !== 'status') {
|
|
20
|
+
throw new YoyoAdminCliUsageError('unsupported-contracts-command', '仅支持 contracts status;未知或多余参数不受支持。');
|
|
21
|
+
}
|
|
22
|
+
if (context.dryRun) {
|
|
23
|
+
throw new YoyoAdminCliUsageError('contracts-dry-run-unsupported', 'contracts status 是只读诊断,不支持 --dry-run。');
|
|
24
|
+
}
|
|
25
|
+
const { repoRoot } = requireWorkspace(context);
|
|
26
|
+
const host = join(repoRoot, 'apps/yoyo-admin/admin-vue');
|
|
27
|
+
const check = (owner, request, live = false) => {
|
|
28
|
+
const command = [request.command, ...request.args].join(' ');
|
|
29
|
+
let status;
|
|
30
|
+
let message;
|
|
31
|
+
try {
|
|
32
|
+
const result = runner(request);
|
|
33
|
+
if (result.error) {
|
|
34
|
+
status = ['ENOENT', 'EACCES', 'EPERM'].includes(result.error.code ?? '') ? 'unavailable' : 'error';
|
|
35
|
+
message = `无法执行 owner check (${result.error.code ?? 'process-error'})。`;
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
status = result.status === 0 ? 'current' : result.status === 3 ? 'stale'
|
|
39
|
+
: live && result.status === 4 ? 'unavailable' : 'error';
|
|
40
|
+
message = status === 'current' ? 'Owner check 确认契约一致。'
|
|
41
|
+
: status === 'stale' ? 'Owner check 检测到契约漂移,请使用正式 owner 生成入口刷新。'
|
|
42
|
+
: status === 'unavailable' ? liveHint : `Owner check 执行失败 (exit ${result.status ?? 'signal'});请单独执行所列命令查看诊断。`;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
const code = error?.code;
|
|
47
|
+
status = ['ENOENT', 'EACCES', 'EPERM'].includes(code ?? '') ? 'unavailable' : 'error';
|
|
48
|
+
message = `Owner runner 执行失败 (${code ?? 'unexpected-error'})。`;
|
|
49
|
+
}
|
|
50
|
+
return { status, owner, command, message };
|
|
51
|
+
};
|
|
52
|
+
const exporter = (name, output) => check(name, {
|
|
53
|
+
command: 'dotnet', cwd: repoRoot,
|
|
54
|
+
args: ['run', '--project', `apps/yoyo-admin/aspnet-core/tools/${name}/${name}.csproj`, '--', '--check', '--output', output],
|
|
55
|
+
});
|
|
56
|
+
const data = {
|
|
57
|
+
kind: 'contracts-status',
|
|
58
|
+
checks: {
|
|
59
|
+
capability: exporter('CapabilityExporter', 'packages/yoyo-admin/admin-core/src/contracts/generated/capability/capability-catalog.json'),
|
|
60
|
+
actionContracts: exporter('ActionContractExporter', 'packages/yoyo-admin/admin-schema/src/generated/action-contract/action-contract-catalog.json'),
|
|
61
|
+
apiGenerated: check('admin-vue api:verify', { command: 'pnpm', args: ['run', 'api:verify'], cwd: host }),
|
|
62
|
+
validationLinkage: check('admin-vue api:validation-linkage', { command: 'pnpm', args: ['run', 'api:validation-linkage'], cwd: host }),
|
|
63
|
+
liveOpenApi: environment.YOYO_ADMIN_OPENAPI_URL?.trim()
|
|
64
|
+
? check('admin-vue api:live-drift', { command: 'pnpm', args: ['run', 'api:live-drift'], cwd: host }, true)
|
|
65
|
+
: { status: 'unavailable', owner: 'admin-vue api:live-drift', command: 'pnpm run api:live-drift', message: liveHint },
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
const current = Object.values(data.checks).every(item => item.status === 'current');
|
|
69
|
+
if (current)
|
|
70
|
+
return ok('contracts status', [], data);
|
|
71
|
+
if (context.parsed.flags.has('--check')) {
|
|
72
|
+
return failure('contracts status', [finding('contracts-not-current', 'error', '契约未全部同步;查看各项状态。')], data);
|
|
73
|
+
}
|
|
74
|
+
return warn('contracts status', [], data);
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
export function isContractsStatusPayload(value) {
|
|
79
|
+
return typeof value === 'object' && value !== null && 'kind' in value && value.kind === 'contracts-status';
|
|
80
|
+
}
|
|
81
|
+
export function renderContractsStatus(data) {
|
|
82
|
+
return Object.entries(data.checks).map(([name, check]) => ` [${check.status}] ${name}\n ${check.message}\n`).join('');
|
|
83
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `create frontend-module` recipe 注册。
|
|
3
|
+
*
|
|
4
|
+
* 解析命令行参数 → 构建 contract-driven FsPlan → dry-run / 事务化落盘。
|
|
5
|
+
*/
|
|
6
|
+
import type { RegisterCreateRecipe } from './create-registry.js';
|
|
7
|
+
export declare function registerFrontendModuleRecipe(register: RegisterCreateRecipe): void;
|
|
8
|
+
//# sourceMappingURL=create-frontend-module.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-frontend-module.d.ts","sourceRoot":"","sources":["../../src/commands/create-frontend-module.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAYH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AAEhE,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,oBAAoB,GAAG,IAAI,CAoDjF"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `create frontend-module` recipe 注册。
|
|
3
|
+
*
|
|
4
|
+
* 解析命令行参数 → 构建 contract-driven FsPlan → dry-run / 事务化落盘。
|
|
5
|
+
*/
|
|
6
|
+
import { requireWorkspace } from '../kernel/command.js';
|
|
7
|
+
import { planRunner } from '../kernel/plan-runner.js';
|
|
8
|
+
import { finding, ok } from '../kernel/result.js';
|
|
9
|
+
import { requireOption, optionalOption } from '../kernel/arguments.js';
|
|
10
|
+
import { STANDARD_CRUD_KIND, planStandardCrudFrontendModule, } from '../generators/frontend-module.js';
|
|
11
|
+
export function registerFrontendModuleRecipe(register) {
|
|
12
|
+
register({
|
|
13
|
+
name: 'frontend-module',
|
|
14
|
+
kinds: [STANDARD_CRUD_KIND],
|
|
15
|
+
summary: '生成 standard-crud 前端模块作者面(manifest.ts + schema.ts)并自动接线 app/manifests.ts。',
|
|
16
|
+
usage: 'yoyo-admin create frontend-module --module <kebab> --resource <camel> --route </path> --title <标题> --permission-prefix <前缀> [--description] [--order] [--group] [--kind] [--datasource] [--virtualization fixed]',
|
|
17
|
+
options: [
|
|
18
|
+
{
|
|
19
|
+
flag: '--kind',
|
|
20
|
+
required: false,
|
|
21
|
+
description: '配方种类,缺省 standard-crud。',
|
|
22
|
+
values: [STANDARD_CRUD_KIND],
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
flag: '--module',
|
|
26
|
+
required: true,
|
|
27
|
+
description: '模块 key(kebab-case),同时作为 app 下模块目录名。',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
flag: '--resource',
|
|
31
|
+
required: true,
|
|
32
|
+
description: '资源 key(camelCase),必须存在于生成的 CRUD resource catalog,例如 measuringUnit。',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
flag: '--datasource',
|
|
36
|
+
required: false,
|
|
37
|
+
description: '显式指定 datasourceId;缺省为 <camel(moduleKey)>.<resourceKey>,schema 与 manifest 共用同一推导。',
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
flag: '--virtualization',
|
|
41
|
+
required: false,
|
|
42
|
+
description: '显式开启固定行虚拟化;省略时关闭,像素预算由 Data 管理。',
|
|
43
|
+
values: ['fixed'],
|
|
44
|
+
},
|
|
45
|
+
{ flag: '--route', required: true, description: '路由路径,以 / 开头。' },
|
|
46
|
+
{ flag: '--title', required: true, description: '页面标题。' },
|
|
47
|
+
{ flag: '--description', required: false, description: '描述,缺省沿用 title。' },
|
|
48
|
+
{ flag: '--order', required: false, description: '菜单排序,缺省 60。' },
|
|
49
|
+
{ flag: '--group', required: false, description: '菜单分组名,缺省「业务模块」。' },
|
|
50
|
+
{
|
|
51
|
+
flag: '--permission-prefix',
|
|
52
|
+
required: true,
|
|
53
|
+
description: '权限前缀,例如 Pages.Administration.BasicData.MeasuringUnits;派生权限必须已在 capability catalog。',
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
run: runCreateFrontendModule,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
function runCreateFrontendModule(context) {
|
|
60
|
+
const datasource = optionalOption(context.parsed, '--datasource');
|
|
61
|
+
const virtualization = optionalOption(context.parsed, '--virtualization');
|
|
62
|
+
const request = {
|
|
63
|
+
moduleKey: requireOption(context.parsed, '--module'),
|
|
64
|
+
resourceKey: requireOption(context.parsed, '--resource'),
|
|
65
|
+
routePath: requireOption(context.parsed, '--route'),
|
|
66
|
+
title: requireOption(context.parsed, '--title'),
|
|
67
|
+
description: optionalOption(context.parsed, '--description') ?? requireOption(context.parsed, '--title'),
|
|
68
|
+
order: Number.parseInt(optionalOption(context.parsed, '--order') ?? '60', 10),
|
|
69
|
+
group: optionalOption(context.parsed, '--group') ?? '业务模块',
|
|
70
|
+
permissionPrefix: requireOption(context.parsed, '--permission-prefix'),
|
|
71
|
+
kind: optionalOption(context.parsed, '--kind') ?? STANDARD_CRUD_KIND,
|
|
72
|
+
...(datasource === undefined ? {} : { datasource }),
|
|
73
|
+
...(virtualization === undefined ? {} : { virtualization }),
|
|
74
|
+
};
|
|
75
|
+
const workspace = requireWorkspace(context);
|
|
76
|
+
const plan = planStandardCrudFrontendModule(workspace, request);
|
|
77
|
+
if (context.dryRun) {
|
|
78
|
+
return ok('create frontend-module', [
|
|
79
|
+
finding('dry-run', 'ok', '未写入任何文件。', `计划 ${plan.summary.creates} 个新文件、${plan.summary.updates} 处更新。`),
|
|
80
|
+
], plan);
|
|
81
|
+
}
|
|
82
|
+
const report = planRunner.preflight(workspace.repoRoot, plan);
|
|
83
|
+
const written = planRunner.apply(workspace.repoRoot, plan);
|
|
84
|
+
return ok('create frontend-module', [
|
|
85
|
+
finding('created', 'ok', `已生成 ${written.length} 个文件变更。`),
|
|
86
|
+
finding('next-step', 'ok', '请在 schema.ts 中补齐展示字段(列/表单控件/查询项);standard-crud 模块由宿主自动发现' +
|
|
87
|
+
'(schema-project-check 从 module definition registry 派生,零测试登记),' +
|
|
88
|
+
'补齐后在 admin-vue 运行 `pnpm run verify` 验证接线。', report.targets.map((target) => target.path).join('\n')),
|
|
89
|
+
], { ...plan, written });
|
|
90
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `create package` recipe 注册。
|
|
3
|
+
*
|
|
4
|
+
* 解析命令行参数 → 构建标准纯 TS 包骨架 FsPlan → dry-run / 事务化落盘 →
|
|
5
|
+
* 受控刷新 workspace lockfile。
|
|
6
|
+
*
|
|
7
|
+
* 闭环顺序(保证「生成后可直接干净安装」):
|
|
8
|
+
* 1. plan 期 fail-closed(kind/目录/包名/冲突)
|
|
9
|
+
* 2. preflight 只读校验
|
|
10
|
+
* 3. 事务化落盘(返回可撤销句柄)
|
|
11
|
+
* 4. `pnpm install --lockfile-only` 补上新的 workspace importer
|
|
12
|
+
* —— 第 4 步失败时撤销第 3 步并 fail-closed,绝不留下「文件已生成但
|
|
13
|
+
* lockfile 没跟上」的半成品。
|
|
14
|
+
*/
|
|
15
|
+
import type { RegisterCreateRecipe } from './create-registry.js';
|
|
16
|
+
export declare function registerPackageRecipe(register: RegisterCreateRecipe): void;
|
|
17
|
+
//# sourceMappingURL=create-package.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-package.d.ts","sourceRoot":"","sources":["../../src/commands/create-package.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AASH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AAEhE,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,oBAAoB,GAAG,IAAI,CAkB1E"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `create package` recipe 注册。
|
|
3
|
+
*
|
|
4
|
+
* 解析命令行参数 → 构建标准纯 TS 包骨架 FsPlan → dry-run / 事务化落盘 →
|
|
5
|
+
* 受控刷新 workspace lockfile。
|
|
6
|
+
*
|
|
7
|
+
* 闭环顺序(保证「生成后可直接干净安装」):
|
|
8
|
+
* 1. plan 期 fail-closed(kind/目录/包名/冲突)
|
|
9
|
+
* 2. preflight 只读校验
|
|
10
|
+
* 3. 事务化落盘(返回可撤销句柄)
|
|
11
|
+
* 4. `pnpm install --lockfile-only` 补上新的 workspace importer
|
|
12
|
+
* —— 第 4 步失败时撤销第 3 步并 fail-closed,绝不留下「文件已生成但
|
|
13
|
+
* lockfile 没跟上」的半成品。
|
|
14
|
+
*/
|
|
15
|
+
import { requireWorkspace } from '../kernel/command.js';
|
|
16
|
+
import { LOCKFILE_RELATIVE, refreshWorkspaceLockfile } from '../kernel/lockfile.js';
|
|
17
|
+
import { planRunner } from '../kernel/plan-runner.js';
|
|
18
|
+
import { finding, ok } from '../kernel/result.js';
|
|
19
|
+
import { requireOption, optionalOption } from '../kernel/arguments.js';
|
|
20
|
+
import { PACKAGE_KIND_TS, planAdminPackage } from '../generators/package.js';
|
|
21
|
+
export function registerPackageRecipe(register) {
|
|
22
|
+
register({
|
|
23
|
+
name: 'package',
|
|
24
|
+
kinds: [PACKAGE_KIND_TS],
|
|
25
|
+
summary: '生成标准纯 TS 共享包骨架(package.json / tsconfig 对 / src 根入口 / smoke test / README),' +
|
|
26
|
+
`并把新包纳入 workspace 的 ${LOCKFILE_RELATIVE} importers。`,
|
|
27
|
+
usage: 'yoyo-admin create package --kind ts --name <kebab> --package-name @yoyoboot/<name> ' +
|
|
28
|
+
'[--description <text>] [--dry-run] [--json]',
|
|
29
|
+
options: [
|
|
30
|
+
{ flag: '--kind', required: true, description: '配方 kind;当前仅支持标准纯 TS 包骨架。', values: [PACKAGE_KIND_TS] },
|
|
31
|
+
{ flag: '--name', required: true, description: '包目录名(kebab-case),生成在 packages/yoyo-admin/ 下。' },
|
|
32
|
+
{ flag: '--package-name', required: true, description: 'npm 包名,必须位于 @yoyoboot/ 作用域下。' },
|
|
33
|
+
{ flag: '--description', required: false, description: '包描述,缺省「Yoyo Admin 共享包。」' },
|
|
34
|
+
],
|
|
35
|
+
run: runCreatePackage,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
function runCreatePackage(context) {
|
|
39
|
+
const request = {
|
|
40
|
+
kind: requireOption(context.parsed, '--kind'),
|
|
41
|
+
directory: requireOption(context.parsed, '--name'),
|
|
42
|
+
packageName: requireOption(context.parsed, '--package-name'),
|
|
43
|
+
description: optionalOption(context.parsed, '--description') ?? 'Yoyo Admin 共享包。',
|
|
44
|
+
};
|
|
45
|
+
const workspace = requireWorkspace(context);
|
|
46
|
+
const plan = planAdminPackage(workspace, request);
|
|
47
|
+
if (context.dryRun) {
|
|
48
|
+
return ok('create package', [
|
|
49
|
+
finding('dry-run', 'ok', '未写入任何文件。', `计划 ${plan.summary.creates} 个新文件:${plan.directory}/` +
|
|
50
|
+
`;真实执行会额外刷新 ${LOCKFILE_RELATIVE} 的 workspace importer。`),
|
|
51
|
+
], plan);
|
|
52
|
+
}
|
|
53
|
+
planRunner.preflight(workspace.repoRoot, plan);
|
|
54
|
+
const transaction = planRunner.applyTransaction(workspace.repoRoot, plan);
|
|
55
|
+
let lockfileChanged;
|
|
56
|
+
try {
|
|
57
|
+
lockfileChanged = refreshWorkspaceLockfile(workspace.adminWorkspace, {
|
|
58
|
+
expectedImporter: request.directory,
|
|
59
|
+
}).changed;
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
// lockfile 刷新失败 = 安装闭环不成立:撤销本次生成,保持工作树原状。
|
|
63
|
+
transaction.rollback();
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
66
|
+
return ok('create package', [
|
|
67
|
+
finding('created', 'ok', `已生成 ${transaction.written.length} 个文件。`),
|
|
68
|
+
finding('lockfile', 'ok', lockfileChanged
|
|
69
|
+
? `已刷新 ${LOCKFILE_RELATIVE}:新包已进入 workspace importers。`
|
|
70
|
+
: `${LOCKFILE_RELATIVE} 已包含该 importer,无需改动。`),
|
|
71
|
+
finding('next-step', 'ok', '请补齐 README 职责说明与首个真实导出,然后运行 ' +
|
|
72
|
+
`\`pnpm --dir .\\packages\\yoyo-admin install\` 链接依赖,` +
|
|
73
|
+
`再运行 \`pnpm --dir .\\packages\\yoyo-admin\\${request.directory} run package:verify\`。`),
|
|
74
|
+
], { ...plan, written: transaction.written, lockfileChanged });
|
|
75
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `create` 的 recipe registry。
|
|
3
|
+
*
|
|
4
|
+
* 这是 create 子命令的**单一能力事实源**:dispatch(按名分发)、
|
|
5
|
+
* capabilities(JSON 投影)、help(usage 渲染)全部从本表读取。
|
|
6
|
+
* 新增 recipe 只改本文件一处,不会出现多处漂移。
|
|
7
|
+
*/
|
|
8
|
+
import type { CommandContext } from '../kernel/command.js';
|
|
9
|
+
import type { YoyoAdminCliResult } from '../kernel/result.js';
|
|
10
|
+
export interface CreateRecipeOption {
|
|
11
|
+
readonly flag: string;
|
|
12
|
+
readonly required: boolean;
|
|
13
|
+
readonly description: string;
|
|
14
|
+
/** 取值选项的候选值(例如 `--kind` 的 profile 列表);help 与 capabilities 共用。 */
|
|
15
|
+
readonly values?: readonly string[];
|
|
16
|
+
}
|
|
17
|
+
export interface CreateRecipeDefinition {
|
|
18
|
+
readonly name: string;
|
|
19
|
+
readonly kinds: readonly string[];
|
|
20
|
+
readonly summary: string;
|
|
21
|
+
readonly usage: string;
|
|
22
|
+
readonly options: readonly CreateRecipeOption[];
|
|
23
|
+
/** recipe 额外声明的布尔 flag(`options` 中的 flag 一律是取值选项)。 */
|
|
24
|
+
readonly booleanFlags?: readonly string[];
|
|
25
|
+
readonly run: (context: CommandContext) => YoyoAdminCliResult;
|
|
26
|
+
}
|
|
27
|
+
/** 注册函数签名;recipe 文件通过它向 registry 注册,避免直接依赖可变单例。 */
|
|
28
|
+
export type RegisterCreateRecipe = (recipe: CreateRecipeDefinition) => void;
|
|
29
|
+
export declare function registerCreateRecipe(recipe: CreateRecipeDefinition): void;
|
|
30
|
+
export declare function resolveCreateRecipe(name: string): CreateRecipeDefinition | undefined;
|
|
31
|
+
export declare function listCreateRecipes(): readonly CreateRecipeDefinition[];
|
|
32
|
+
//# sourceMappingURL=create-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-registry.d.ts","sourceRoot":"","sources":["../../src/commands/create-registry.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAC1D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAE7D,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,iEAAiE;IACjE,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CACpC;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,OAAO,EAAE,SAAS,kBAAkB,EAAE,CAAA;IAC/C,sDAAsD;IACtD,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACzC,QAAQ,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,kBAAkB,CAAA;CAC9D;AAID,mDAAmD;AACnD,MAAM,MAAM,oBAAoB,GAAG,CAAC,MAAM,EAAE,sBAAsB,KAAK,IAAI,CAAA;AAE3E,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,sBAAsB,GAAG,IAAI,CAKzE;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,sBAAsB,GAAG,SAAS,CAEpF;AAED,wBAAgB,iBAAiB,IAAI,SAAS,sBAAsB,EAAE,CAErE"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `create` 的 recipe registry。
|
|
3
|
+
*
|
|
4
|
+
* 这是 create 子命令的**单一能力事实源**:dispatch(按名分发)、
|
|
5
|
+
* capabilities(JSON 投影)、help(usage 渲染)全部从本表读取。
|
|
6
|
+
* 新增 recipe 只改本文件一处,不会出现多处漂移。
|
|
7
|
+
*/
|
|
8
|
+
const recipes = new Map();
|
|
9
|
+
export function registerCreateRecipe(recipe) {
|
|
10
|
+
if (recipes.has(recipe.name)) {
|
|
11
|
+
throw new Error(`Create recipe '${recipe.name}' is already registered.`);
|
|
12
|
+
}
|
|
13
|
+
recipes.set(recipe.name, recipe);
|
|
14
|
+
}
|
|
15
|
+
export function resolveCreateRecipe(name) {
|
|
16
|
+
return recipes.get(name);
|
|
17
|
+
}
|
|
18
|
+
export function listCreateRecipes() {
|
|
19
|
+
return [...recipes.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
20
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `yoyo-admin create <recipe>`
|
|
3
|
+
*
|
|
4
|
+
* 作者操作的统一入口。
|
|
5
|
+
*
|
|
6
|
+
* 生命周期(保证零部分写入):
|
|
7
|
+
* 1. 构建完整 FsPlan(含新文件、接线更新)
|
|
8
|
+
* 2. preflight 只读校验全部目标
|
|
9
|
+
* 3. 全部通过后事务化落盘(失败自动回滚)
|
|
10
|
+
*
|
|
11
|
+
* `--dry-run` 在第 1 步后停止,只渲染计划。
|
|
12
|
+
*
|
|
13
|
+
* recipe 的注册表见 `create-registry.ts`:dispatch、capabilities、help、
|
|
14
|
+
* 参数校验共用,本文件不再维护手工 recipe 列表。
|
|
15
|
+
*/
|
|
16
|
+
import { type CommandContext } from '../kernel/command.js';
|
|
17
|
+
import { type RecipeHelpSurface } from '../kernel/help.js';
|
|
18
|
+
import { type YoyoAdminCliResult } from '../kernel/result.js';
|
|
19
|
+
export declare function runCreate(context: CommandContext): YoyoAdminCliResult;
|
|
20
|
+
/** `create` 的 help 投影面:与 inspect 共用同一套渲染器,只有"面"不同。 */
|
|
21
|
+
export declare const CREATE_HELP_SURFACE: RecipeHelpSurface;
|
|
22
|
+
export declare const createCommand: import("../index.js").RegisteredCommand;
|
|
23
|
+
//# sourceMappingURL=create.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/commands/create.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAA;AACzE,OAAO,EAGL,KAAK,iBAAiB,EACvB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAA0B,KAAK,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAgBrF,wBAAgB,SAAS,CAAC,OAAO,EAAE,cAAc,GAAG,kBAAkB,CAYrE;AAaD,sDAAsD;AACtD,eAAO,MAAM,mBAAmB,EAAE,iBAMjC,CAAA;AAyDD,eAAO,MAAM,aAAa,yCAaxB,CAAA"}
|