kiro-spec-engine 1.38.0 → 1.40.0

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 CHANGED
@@ -7,6 +7,44 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.40.0] - 2026-02-10
11
+
12
+ ### Added
13
+ - **Moqui Scene Template Extractor**: Extract reusable scene templates from live Moqui ERP instances
14
+ - `MoquiExtractor` — Analyze discovered Moqui resources, identify business patterns (crud/query/workflow), generate scene template bundles
15
+ - Built-in YAML serializer for scene manifests (`kse.scene/v0.2` apiVersion)
16
+ - Entity grouping by Header/Item suffix patterns (e.g., OrderHeader + OrderItem → composite pattern)
17
+ - Pattern-based manifest generation with governance contracts (risk_level, approval, idempotency)
18
+ - Package contract generation (`kse.scene.package/v0.1` apiVersion) with template parameters
19
+ - Template bundle file writing with partial failure resilience
20
+ - `kse scene extract` — Extract scene templates from Moqui ERP instance
21
+ - `--config <path>` custom adapter config path
22
+ - `--type <type>` filter discovery by resource type (entities|services|screens)
23
+ - `--pattern <pattern>` filter by business pattern (crud|query|workflow)
24
+ - `--out <dir>` output directory for template bundles
25
+ - `--dry-run` preview extraction without writing files
26
+ - `--json` structured JSON output
27
+
28
+ ### Fixed
29
+ - **scene discover**: Fixed `response.body.data` → `response.data` property access for Moqui catalog endpoint responses
30
+
31
+ ## [1.39.0] - 2026-02-10
32
+
33
+ ### Added
34
+ - **Moqui ERP Adapter**: Integrate Moqui ERP instance into KSE scene runtime
35
+ - `MoquiClient` — HTTP client with JWT auth lifecycle (login, refresh, re-login, logout), retry logic using Node.js built-in `http`/`https`
36
+ - `MoquiAdapter` — Binding handler for `spec.erp.*` and `moqui.*` refs, entity CRUD, service invocation, screen discovery
37
+ - `kse scene connect` — Test connectivity and authentication to Moqui ERP instance
38
+ - `--config <path>` custom adapter config path
39
+ - `--json` structured JSON output
40
+ - `kse scene discover` — Discover available entities, services, and screens from Moqui ERP
41
+ - `--config <path>` custom adapter config path
42
+ - `--type <type>` filter by catalog type (entities|services|screens)
43
+ - `--json` structured JSON output
44
+
45
+ ### Fixed
46
+ - **Jest forceExit**: Added `forceExit: true` to jest configs to prevent CI hang from leaked worker processes
47
+
10
48
  ## [1.38.0] - 2026-02-10
11
49
 
12
50
  ### Added
package/README.md CHANGED
@@ -310,6 +310,15 @@ Structure your work with Requirements → Design → Tasks workflow
310
310
  - **Cross-Platform**: Consistent path handling across Windows/Linux/macOS
311
311
  - **Smart Exclusions**: Automatically skip common non-repository directories (node_modules, build, etc.)
312
312
 
313
+ ### Moqui ERP Integration 🚀 NEW in v1.39.0
314
+ - **Moqui ERP Adapter**: Connect KSE scene runtime to live Moqui ERP instances
315
+ - `MoquiClient` — HTTP client with JWT auth lifecycle (login, refresh, re-login, logout) and retry logic
316
+ - `MoquiAdapter` — Binding handler for `spec.erp.*` and `moqui.*` refs, entity CRUD, service invocation, screen discovery
317
+ - **Scene Template Extractor** (v1.40.0): Analyze Moqui resources, identify business patterns, generate reusable scene templates
318
+ - Entity grouping by Header/Item suffix patterns (e.g., OrderHeader + OrderItem → composite)
319
+ - Pattern-based manifest generation with governance contracts
320
+ - **CLI Commands**: `scene connect`, `scene discover`, `scene extract` with `--json` support
321
+
313
322
  ### Scene Template Engine 🚀 NEW in v1.25.0
314
323
  - **Template Variable Schema**: Define typed variables (string, number, boolean, enum, array) with validation rules in scene-package.json
315
324
  - **Multi-File Rendering**: Recursive template processing with `{{variable}}` substitution, `{{#if}}` conditionals, `{{#each}}` loops
@@ -437,6 +446,11 @@ kse scene template-validate --package <path> # Validate template variable sche
437
446
  kse scene template-resolve --package <name> # Resolve inheritance chain and merged schema
438
447
  kse scene template-render --package <name> --values <json> --out <dir> # Render template files
439
448
 
449
+ # Moqui ERP integration (NEW in v1.39.0)
450
+ kse scene connect --config <path> # Test connectivity to Moqui ERP instance
451
+ kse scene discover --config <path> # Discover entities, services, screens from Moqui
452
+ kse scene extract --config <path> --out <dir> # Extract scene templates from Moqui (v1.40.0)
453
+
440
454
  # DevOps operations
441
455
  kse ops init <project-name> # Initialize operations specs
442
456
  kse ops validate [<project>] # Validate operations completeness
package/README.zh.md CHANGED
@@ -266,6 +266,15 @@ sequenceDiagram
266
266
  - **命令执行**:在特定环境上下文中运行命令
267
267
  - **跨平台**:在 Windows、Linux 和 macOS 上无缝工作
268
268
 
269
+ ### Moqui ERP 集成 🚀 v1.39.0 新增
270
+ - **Moqui ERP 适配器**: 将 KSE 场景运行时连接到 Moqui ERP 实例
271
+ - `MoquiClient` — 支持 JWT 认证生命周期(登录、刷新、重登录、登出)和重试逻辑的 HTTP 客户端
272
+ - `MoquiAdapter` — 处理 `spec.erp.*` 和 `moqui.*` 引用的绑定处理器,支持实体 CRUD、服务调用、屏幕发现
273
+ - **场景模板提取器**(v1.40.0): 分析 Moqui 资源,识别业务模式,生成可复用场景模板
274
+ - 按 Header/Item 后缀模式分组实体(如 OrderHeader + OrderItem → 复合模式)
275
+ - 基于模式的清单生成,包含治理合约
276
+ - **CLI 命令**: `scene connect`、`scene discover`、`scene extract`,支持 `--json` 输出
277
+
269
278
  ### 场景模板引擎 🚀 v1.25.0 新增
270
279
  - **模板变量 Schema**: 在 scene-package.json 中定义类型化变量(string, number, boolean, enum, array)及验证规则
271
280
  - **多文件渲染**: 递归模板处理,支持 `{{variable}}` 替换、`{{#if}}` 条件、`{{#each}}` 循环
@@ -341,6 +350,11 @@ kse scene template-validate --package <path> # 验证模板变量 schema
341
350
  kse scene template-resolve --package <name> # 解析继承链和合并 schema
342
351
  kse scene template-render --package <name> --values <json> --out <dir> # 渲染模板文件
343
352
 
353
+ # Moqui ERP 集成 (v1.39.0 新增)
354
+ kse scene connect --config <path> # 测试 Moqui ERP 实例连接
355
+ kse scene discover --config <path> # 发现 Moqui 实体、服务、屏幕
356
+ kse scene extract --config <path> --out <dir> # 从 Moqui 提取场景模板 (v1.40.0)
357
+
344
358
  # DevOps 运维
345
359
  kse ops init <project-name> # 初始化运维 specs
346
360
  kse ops validate [<project>] # 验证运维完整性
@@ -214,6 +214,23 @@ kse scene template-render --package <name> --values <json-or-path> --out <dir>
214
214
  kse scene template-render --package scene-erp --values '{"entity_name":"Order"}' --out ./output --json
215
215
  ```
216
216
 
217
+ ### Moqui ERP Integration
218
+
219
+ ```bash
220
+ # Test connectivity and authentication to Moqui ERP instance
221
+ kse scene connect --config <path>
222
+ kse scene connect --config ./moqui-config.json --json
223
+
224
+ # Discover available entities, services, and screens from Moqui ERP
225
+ kse scene discover --config <path>
226
+ kse scene discover --config ./moqui-config.json --type entities --json
227
+
228
+ # Extract scene templates from Moqui ERP instance
229
+ kse scene extract --config <path> --out <dir>
230
+ kse scene extract --config ./moqui-config.json --type entities --pattern crud --out ./templates --json
231
+ kse scene extract --config ./moqui-config.json --dry-run --json
232
+ ```
233
+
217
234
  ### Version & Upgrade
218
235
 
219
236
  ```bash
@@ -11,6 +11,9 @@ const PlanCompiler = require('../scene-runtime/plan-compiler');
11
11
  const PolicyGate = require('../scene-runtime/policy-gate');
12
12
  const RuntimeExecutor = require('../scene-runtime/runtime-executor');
13
13
  const EvalBridge = require('../scene-runtime/eval-bridge');
14
+ const MoquiClient = require('../scene-runtime/moqui-client');
15
+ const { loadAdapterConfig, validateAdapterConfig } = require('../scene-runtime/moqui-adapter');
16
+ const { runExtraction, SUPPORTED_PATTERNS: EXTRACTOR_SUPPORTED_PATTERNS } = require('../scene-runtime/moqui-extractor');
14
17
 
15
18
  const RUN_MODES = new Set(['dry_run', 'commit']);
16
19
  const SCAFFOLD_TYPES = new Set(['erp', 'hybrid']);
@@ -728,6 +731,42 @@ function registerSceneCommands(program) {
728
731
  .action(async (options) => {
729
732
  await runSceneLockCommand({ ...options, action: 'ls' });
730
733
  });
734
+
735
+ // ── scene connect ──
736
+ sceneCmd
737
+ .command('connect')
738
+ .description('Test connectivity and authentication to a Moqui ERP instance')
739
+ .option('-c, --config <path>', 'Path to moqui-adapter.json config file')
740
+ .option('-r, --registry <path>', 'Registry root directory', '.kiro/registry')
741
+ .option('--json', 'Print result as JSON')
742
+ .action(async (options) => {
743
+ await runSceneConnectCommand(options);
744
+ });
745
+
746
+ // ── scene discover ──
747
+ sceneCmd
748
+ .command('discover')
749
+ .description('Discover available entities, services, and screens from Moqui ERP')
750
+ .option('-c, --config <path>', 'Path to moqui-adapter.json config file')
751
+ .option('-t, --type <type>', 'Catalog type to discover (entities|services|screens)')
752
+ .option('--json', 'Print result as JSON')
753
+ .action(async (options) => {
754
+ await runSceneDiscoverCommand(options);
755
+ });
756
+
757
+ // ── scene extract ──
758
+ sceneCmd
759
+ .command('extract')
760
+ .description('Extract scene templates from a Moqui ERP instance')
761
+ .option('-c, --config <path>', 'Path to moqui-adapter.json config file')
762
+ .option('-t, --type <type>', 'Resource type filter (entities|services|screens)')
763
+ .option('-p, --pattern <pattern>', 'Pattern filter (crud|query|workflow)')
764
+ .option('-o, --out <dir>', 'Output directory for template bundles')
765
+ .option('--dry-run', 'Preview extraction without writing files')
766
+ .option('--json', 'Print result as JSON')
767
+ .action(async (options) => {
768
+ await runSceneExtractCommand(options);
769
+ });
731
770
  }
732
771
 
733
772
  function normalizeSourceOptions(options = {}) {
@@ -11993,6 +12032,334 @@ function printSceneLockSummary(options, payload) {
11993
12032
  }
11994
12033
  }
11995
12034
 
12035
+ // ── Scene Connect ─────────────────────────────────────────────────────────
12036
+
12037
+ function normalizeSceneConnectOptions(options = {}) {
12038
+ return {
12039
+ config: options.config ? String(options.config).trim() : undefined,
12040
+ registry: options.registry ? String(options.registry).trim() : '.kiro/registry',
12041
+ json: options.json === true
12042
+ };
12043
+ }
12044
+
12045
+ function validateSceneConnectOptions(options) {
12046
+ return null;
12047
+ }
12048
+
12049
+ async function runSceneConnectCommand(rawOptions = {}, dependencies = {}) {
12050
+ const projectRoot = dependencies.projectRoot || process.cwd();
12051
+ const fileSystem = dependencies.fileSystem || fs;
12052
+
12053
+ const options = normalizeSceneConnectOptions(rawOptions);
12054
+ const validationError = validateSceneConnectOptions(options);
12055
+
12056
+ if (validationError) {
12057
+ console.error(chalk.red(`Scene connect failed: ${validationError}`));
12058
+ process.exitCode = 1;
12059
+ return null;
12060
+ }
12061
+
12062
+ let client = null;
12063
+ try {
12064
+ const configResult = loadAdapterConfig(options.config, projectRoot);
12065
+ if (configResult.error) {
12066
+ throw new Error(configResult.error);
12067
+ }
12068
+
12069
+ const validation = validateAdapterConfig(configResult.config);
12070
+ if (!validation.valid) {
12071
+ throw new Error(`config validation failed: ${validation.errors.join(', ')}`);
12072
+ }
12073
+
12074
+ const config = configResult.config;
12075
+ client = new MoquiClient(config);
12076
+ const loginResult = await client.login();
12077
+
12078
+ let payload;
12079
+ if (loginResult.success) {
12080
+ payload = {
12081
+ success: true,
12082
+ baseUrl: config.baseUrl,
12083
+ authStatus: 'authenticated'
12084
+ };
12085
+ } else {
12086
+ payload = {
12087
+ success: false,
12088
+ baseUrl: config.baseUrl,
12089
+ error: { code: 'AUTH_FAILED', message: loginResult.error || 'authentication failed' }
12090
+ };
12091
+ }
12092
+
12093
+ printSceneConnectSummary(options, payload);
12094
+ return payload;
12095
+ } catch (error) {
12096
+ const payload = {
12097
+ success: false,
12098
+ baseUrl: rawOptions.config || 'unknown',
12099
+ error: { code: 'CONNECT_FAILED', message: error.message }
12100
+ };
12101
+ printSceneConnectSummary(options, payload);
12102
+ process.exitCode = 1;
12103
+ return payload;
12104
+ } finally {
12105
+ if (client) {
12106
+ try { await client.dispose(); } catch (_) { /* ignore dispose errors */ }
12107
+ }
12108
+ }
12109
+ }
12110
+
12111
+ function printSceneConnectSummary(options, payload) {
12112
+ if (options.json) {
12113
+ console.log(JSON.stringify(payload, null, 2));
12114
+ return;
12115
+ }
12116
+
12117
+ if (payload.success) {
12118
+ console.log(chalk.green(`Connected to Moqui ERP at ${payload.baseUrl}`));
12119
+ console.log(` Auth status: ${payload.authStatus}`);
12120
+ } else {
12121
+ console.error(chalk.red(`Connection failed: ${payload.error.message}`));
12122
+ if (payload.baseUrl && payload.baseUrl !== 'unknown') {
12123
+ console.error(` Target: ${payload.baseUrl}`);
12124
+ }
12125
+ }
12126
+ }
12127
+
12128
+ // ── Scene Discover ────────────────────────────────────────────────────────
12129
+
12130
+ function normalizeSceneDiscoverOptions(options = {}) {
12131
+ return {
12132
+ config: options.config ? String(options.config).trim() : undefined,
12133
+ type: options.type ? String(options.type).trim() : undefined,
12134
+ json: options.json === true
12135
+ };
12136
+ }
12137
+
12138
+ function validateSceneDiscoverOptions(options) {
12139
+ if (options.type && !['entities', 'services', 'screens'].includes(options.type)) {
12140
+ return `invalid --type "${options.type}", must be entities, services, or screens`;
12141
+ }
12142
+ return null;
12143
+ }
12144
+
12145
+ async function runSceneDiscoverCommand(rawOptions = {}, dependencies = {}) {
12146
+ const projectRoot = dependencies.projectRoot || process.cwd();
12147
+ const fileSystem = dependencies.fileSystem || fs;
12148
+
12149
+ const options = normalizeSceneDiscoverOptions(rawOptions);
12150
+ const validationError = validateSceneDiscoverOptions(options);
12151
+
12152
+ if (validationError) {
12153
+ console.error(chalk.red(`Scene discover failed: ${validationError}`));
12154
+ process.exitCode = 1;
12155
+ return null;
12156
+ }
12157
+
12158
+ let client = null;
12159
+ try {
12160
+ const configResult = loadAdapterConfig(options.config, projectRoot);
12161
+ if (configResult.error) {
12162
+ throw new Error(configResult.error);
12163
+ }
12164
+
12165
+ const validation = validateAdapterConfig(configResult.config);
12166
+ if (!validation.valid) {
12167
+ throw new Error(`config validation failed: ${validation.errors.join(', ')}`);
12168
+ }
12169
+
12170
+ const config = configResult.config;
12171
+ client = new MoquiClient(config);
12172
+ const loginResult = await client.login();
12173
+
12174
+ if (!loginResult.success) {
12175
+ throw new Error(loginResult.error || 'authentication failed');
12176
+ }
12177
+
12178
+ let payload;
12179
+
12180
+ if (options.type) {
12181
+ // Query specific catalog type
12182
+ const endpoint = options.type === 'entities'
12183
+ ? '/api/v1/entities'
12184
+ : options.type === 'services'
12185
+ ? '/api/v1/services'
12186
+ : '/api/v1/screens';
12187
+
12188
+ const response = await client.request('GET', endpoint);
12189
+ const rawData = (response && response.data) || {};
12190
+ const items = Array.isArray(rawData) ? rawData : (rawData[options.type] || rawData.items || []);
12191
+ payload = {
12192
+ success: true,
12193
+ type: options.type,
12194
+ [options.type]: Array.isArray(items) ? items : [],
12195
+ count: Array.isArray(items) ? items.length : 0
12196
+ };
12197
+ } else {
12198
+ // Query all catalog types for summary
12199
+ const [entitiesRes, servicesRes, screensRes] = await Promise.all([
12200
+ client.request('GET', '/api/v1/entities').catch(() => null),
12201
+ client.request('GET', '/api/v1/services').catch(() => null),
12202
+ client.request('GET', '/api/v1/screens').catch(() => null)
12203
+ ]);
12204
+
12205
+ const entitiesRaw = (entitiesRes && entitiesRes.data) || {};
12206
+ const servicesRaw = (servicesRes && servicesRes.data) || {};
12207
+ const screensRaw = (screensRes && screensRes.data) || {};
12208
+ const entitiesData = Array.isArray(entitiesRaw) ? entitiesRaw : (entitiesRaw.entities || entitiesRaw.items || []);
12209
+ const servicesData = Array.isArray(servicesRaw) ? servicesRaw : (servicesRaw.services || servicesRaw.items || []);
12210
+ const screensData = Array.isArray(screensRaw) ? screensRaw : (screensRaw.screens || screensRaw.items || []);
12211
+
12212
+ payload = {
12213
+ success: true,
12214
+ summary: {
12215
+ entities: { count: Array.isArray(entitiesData) ? entitiesData.length : 0 },
12216
+ services: { count: Array.isArray(servicesData) ? servicesData.length : 0 },
12217
+ screens: { count: Array.isArray(screensData) ? screensData.length : 0 }
12218
+ }
12219
+ };
12220
+ }
12221
+
12222
+ printSceneDiscoverSummary(options, payload);
12223
+ return payload;
12224
+ } catch (error) {
12225
+ const payload = {
12226
+ success: false,
12227
+ error: { code: 'DISCOVER_FAILED', message: error.message }
12228
+ };
12229
+ printSceneDiscoverSummary(options, payload);
12230
+ process.exitCode = 1;
12231
+ return payload;
12232
+ } finally {
12233
+ if (client) {
12234
+ try { await client.dispose(); } catch (_) { /* ignore dispose errors */ }
12235
+ }
12236
+ }
12237
+ }
12238
+
12239
+ function printSceneDiscoverSummary(options, payload) {
12240
+ if (options.json) {
12241
+ console.log(JSON.stringify(payload, null, 2));
12242
+ return;
12243
+ }
12244
+
12245
+ if (!payload.success) {
12246
+ console.error(chalk.red(`Discovery failed: ${payload.error.message}`));
12247
+ return;
12248
+ }
12249
+
12250
+ if (payload.summary) {
12251
+ console.log(chalk.green('Moqui ERP catalog summary:'));
12252
+ console.log(` Entities: ${payload.summary.entities.count}`);
12253
+ console.log(` Services: ${payload.summary.services.count}`);
12254
+ console.log(` Screens: ${payload.summary.screens.count}`);
12255
+ } else {
12256
+ console.log(chalk.green(`Discovered ${payload.count} ${payload.type}:`));
12257
+ const items = payload[payload.type] || [];
12258
+ for (const item of items.slice(0, 50)) {
12259
+ const name = typeof item === 'string' ? item : (item.name || item.entityName || JSON.stringify(item));
12260
+ console.log(` ${name}`);
12261
+ }
12262
+ if (items.length > 50) {
12263
+ console.log(` ... and ${items.length - 50} more`);
12264
+ }
12265
+ }
12266
+ }
12267
+
12268
+ // ─── Scene Extract CLI Functions ──────────────────────────────────
12269
+
12270
+ function normalizeSceneExtractOptions(options = {}) {
12271
+ return {
12272
+ config: options.config ? String(options.config).trim() : undefined,
12273
+ type: options.type ? String(options.type).trim() : undefined,
12274
+ pattern: options.pattern ? String(options.pattern).trim() : undefined,
12275
+ out: options.out ? String(options.out).trim() : '.kiro/templates/extracted',
12276
+ dryRun: options.dryRun === true,
12277
+ json: options.json === true
12278
+ };
12279
+ }
12280
+
12281
+ function validateSceneExtractOptions(options) {
12282
+ if (options.type && !['entities', 'services', 'screens'].includes(options.type)) {
12283
+ return `invalid --type "${options.type}", must be entities, services, or screens`;
12284
+ }
12285
+ if (options.pattern && !EXTRACTOR_SUPPORTED_PATTERNS.includes(options.pattern)) {
12286
+ return `invalid --pattern "${options.pattern}", must be one of: ${EXTRACTOR_SUPPORTED_PATTERNS.join(', ')}`;
12287
+ }
12288
+ return null;
12289
+ }
12290
+
12291
+ function printSceneExtractSummary(options, payload) {
12292
+ if (options.json) {
12293
+ console.log(JSON.stringify(payload, null, 2));
12294
+ return;
12295
+ }
12296
+
12297
+ if (!payload.success) {
12298
+ console.error(chalk.red(`Extraction failed: ${payload.error.message}`));
12299
+ return;
12300
+ }
12301
+
12302
+ const summary = payload.summary || {};
12303
+ const patterns = summary.patterns || {};
12304
+
12305
+ console.log(chalk.green(`Extracted ${summary.totalTemplates || 0} template(s):`));
12306
+ console.log(` CRUD: ${patterns.crud || 0}`);
12307
+ console.log(` Query: ${patterns.query || 0}`);
12308
+ console.log(` Workflow: ${patterns.workflow || 0}`);
12309
+
12310
+ if (options.dryRun) {
12311
+ console.log(chalk.yellow(' (dry-run — no files written)'));
12312
+ } else {
12313
+ console.log(` Output: ${summary.outputDir || options.out}`);
12314
+ }
12315
+
12316
+ if (payload.warnings && payload.warnings.length > 0) {
12317
+ console.log(chalk.yellow(` Warnings: ${payload.warnings.length}`));
12318
+ for (const w of payload.warnings) {
12319
+ console.log(chalk.yellow(` - ${w}`));
12320
+ }
12321
+ }
12322
+ }
12323
+
12324
+ async function runSceneExtractCommand(rawOptions = {}, dependencies = {}) {
12325
+ const options = normalizeSceneExtractOptions(rawOptions);
12326
+ const validationError = validateSceneExtractOptions(options);
12327
+
12328
+ if (validationError) {
12329
+ console.error(chalk.red(validationError));
12330
+ process.exitCode = 1;
12331
+ return null;
12332
+ }
12333
+
12334
+ try {
12335
+ const payload = await runExtraction({
12336
+ config: options.config,
12337
+ type: options.type,
12338
+ pattern: options.pattern,
12339
+ out: options.out,
12340
+ dryRun: options.dryRun
12341
+ }, dependencies);
12342
+
12343
+ if (!payload.success) {
12344
+ process.exitCode = 1;
12345
+ }
12346
+
12347
+ printSceneExtractSummary(options, payload);
12348
+ return payload;
12349
+ } catch (error) {
12350
+ const payload = {
12351
+ success: false,
12352
+ templates: [],
12353
+ summary: { totalTemplates: 0, patterns: { crud: 0, query: 0, workflow: 0 }, outputDir: options.out },
12354
+ warnings: [],
12355
+ error: { code: 'EXTRACT_FAILED', message: error.message }
12356
+ };
12357
+ printSceneExtractSummary(options, payload);
12358
+ process.exitCode = 1;
12359
+ return payload;
12360
+ }
12361
+ }
12362
+
11996
12363
  module.exports = {
11997
12364
  RUN_MODES,
11998
12365
  SCAFFOLD_TYPES,
@@ -12185,5 +12552,17 @@ module.exports = {
12185
12552
  normalizeSceneLockOptions,
12186
12553
  validateSceneLockOptions,
12187
12554
  runSceneLockCommand,
12188
- printSceneLockSummary
12555
+ printSceneLockSummary,
12556
+ normalizeSceneConnectOptions,
12557
+ validateSceneConnectOptions,
12558
+ runSceneConnectCommand,
12559
+ printSceneConnectSummary,
12560
+ normalizeSceneDiscoverOptions,
12561
+ validateSceneDiscoverOptions,
12562
+ runSceneDiscoverCommand,
12563
+ printSceneDiscoverSummary,
12564
+ normalizeSceneExtractOptions,
12565
+ validateSceneExtractOptions,
12566
+ runSceneExtractCommand,
12567
+ printSceneExtractSummary
12189
12568
  };