scene-capability-engine 3.6.57 → 3.6.59
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 +18 -0
- package/README.md +5 -3
- package/README.zh.md +5 -3
- package/bin/scene-capability-engine.js +2 -0
- package/docs/command-reference.md +72 -0
- package/docs/magicball-adaptation-task-checklist-v1.md +65 -10
- package/docs/magicball-cli-invocation-examples.md +53 -8
- package/docs/magicball-engineering-projection-contract.md +175 -0
- package/docs/magicball-frontend-state-and-command-mapping.md +42 -5
- package/docs/magicball-integration-doc-index.md +19 -5
- package/docs/magicball-integration-issue-tracker.md +15 -5
- package/docs/magicball-mode-home-and-ontology-empty-state-playbook.md +13 -5
- package/docs/magicball-project-portfolio-contract.md +216 -0
- package/docs/magicball-sce-adaptation-guide.md +18 -4
- package/docs/magicball-ui-surface-checklist.md +25 -0
- package/docs/magicball-write-auth-adaptation-guide.md +3 -1
- package/docs/release-checklist.md +8 -0
- package/docs/releases/README.md +2 -0
- package/docs/releases/v3.6.58.md +27 -0
- package/docs/releases/v3.6.59.md +18 -0
- package/docs/zh/release-checklist.md +8 -0
- package/docs/zh/releases/README.md +2 -0
- package/docs/zh/releases/v3.6.58.md +27 -0
- package/docs/zh/releases/v3.6.59.md +18 -0
- package/lib/app/engineering-scaffold-service.js +154 -0
- package/lib/commands/app.js +442 -13
- package/lib/commands/project.js +105 -0
- package/lib/commands/scene.js +16 -0
- package/lib/project/portfolio-projection-service.js +389 -0
- package/lib/project/supervision-projection-service.js +329 -0
- package/lib/project/target-resolution-service.js +180 -0
- package/lib/scene/delivery-projection-service.js +650 -0
- package/package.json +6 -2
- package/scripts/magicball-engineering-contract-audit.js +347 -0
- package/scripts/magicball-project-contract-audit.js +254 -0
- package/template/.sce/README.md +2 -2
package/lib/commands/app.js
CHANGED
|
@@ -10,6 +10,7 @@ const { getCurrentDeviceProfile } = require('../device/current-device');
|
|
|
10
10
|
const { loadDeviceOverride } = require('../device/device-override-store');
|
|
11
11
|
const { loadAppRegistryConfig, saveAppRegistryConfig } = require('../app/registry-config');
|
|
12
12
|
const { syncBundleRegistry, syncServiceCatalog } = require('../app/registry-sync-service');
|
|
13
|
+
const { scaffoldEngineeringWorkspace } = require('../app/engineering-scaffold-service');
|
|
13
14
|
|
|
14
15
|
function normalizeString(value) {
|
|
15
16
|
if (typeof value !== 'string') {
|
|
@@ -213,27 +214,311 @@ function buildAppInstallStateItem(bundle = {}, currentDevice = null) {
|
|
|
213
214
|
};
|
|
214
215
|
}
|
|
215
216
|
|
|
216
|
-
|
|
217
|
+
const ENGINEERING_PREVIEW_REASON_CODES = Object.freeze({
|
|
218
|
+
SOURCE_MISSING: 'engineering.source_missing',
|
|
219
|
+
PROJECTION_MISSING: 'engineering.projection_missing',
|
|
220
|
+
WORKSPACE_UNAVAILABLE: 'engineering.workspace_unavailable',
|
|
221
|
+
HYDRATE_REQUIRED: 'engineering.hydrate_required',
|
|
222
|
+
ACTIVATE_REQUIRED: 'engineering.activate_required',
|
|
223
|
+
UPSTREAM_CONTRACT_MISSING: 'engineering.upstream_contract_missing'
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
function buildEngineeringReadinessReasonCodes(graph = {}) {
|
|
217
227
|
const bundle = graph.bundle || {};
|
|
218
228
|
const engineeringProject = graph.engineering_project || {};
|
|
219
229
|
const metadata = engineeringProject.metadata && typeof engineeringProject.metadata === 'object'
|
|
220
230
|
? engineeringProject.metadata
|
|
221
231
|
: {};
|
|
232
|
+
const attached = Boolean(
|
|
233
|
+
engineeringProject.engineering_project_id
|
|
234
|
+
|| engineeringProject.repo_url
|
|
235
|
+
|| engineeringProject.project_key
|
|
236
|
+
|| engineeringProject.project_name
|
|
237
|
+
);
|
|
238
|
+
const sourceKnown = Boolean(normalizeString(engineeringProject.repo_url));
|
|
239
|
+
const workspacePath = normalizeString(engineeringProject.workspace_path) || null;
|
|
240
|
+
const hydrated = Boolean(metadata.hydration && metadata.hydration.status === 'ready');
|
|
241
|
+
const active = Boolean(metadata.activation && metadata.activation.active === true);
|
|
242
|
+
const reasonCodes = [];
|
|
243
|
+
|
|
244
|
+
if (!attached || !sourceKnown) {
|
|
245
|
+
reasonCodes.push(ENGINEERING_PREVIEW_REASON_CODES.SOURCE_MISSING);
|
|
246
|
+
}
|
|
247
|
+
if (!workspacePath) {
|
|
248
|
+
reasonCodes.push(ENGINEERING_PREVIEW_REASON_CODES.PROJECTION_MISSING);
|
|
249
|
+
reasonCodes.push(ENGINEERING_PREVIEW_REASON_CODES.WORKSPACE_UNAVAILABLE);
|
|
250
|
+
} else if (!hydrated) {
|
|
251
|
+
reasonCodes.push(ENGINEERING_PREVIEW_REASON_CODES.HYDRATE_REQUIRED);
|
|
252
|
+
} else if (!active) {
|
|
253
|
+
reasonCodes.push(ENGINEERING_PREVIEW_REASON_CODES.ACTIVATE_REQUIRED);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return Array.from(new Set(reasonCodes));
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function buildEngineeringPreviewNextActions(graph = {}) {
|
|
260
|
+
const engineeringProject = graph.engineering_project || {};
|
|
261
|
+
const metadata = engineeringProject.metadata && typeof engineeringProject.metadata === 'object'
|
|
262
|
+
? engineeringProject.metadata
|
|
263
|
+
: {};
|
|
264
|
+
const attached = Boolean(
|
|
265
|
+
engineeringProject.engineering_project_id
|
|
266
|
+
|| engineeringProject.repo_url
|
|
267
|
+
|| engineeringProject.project_key
|
|
268
|
+
|| engineeringProject.project_name
|
|
269
|
+
);
|
|
270
|
+
const sourceKnown = Boolean(normalizeString(engineeringProject.repo_url));
|
|
271
|
+
const workspacePath = normalizeString(engineeringProject.workspace_path) || null;
|
|
272
|
+
const hydrated = Boolean(metadata.hydration && metadata.hydration.status === 'ready');
|
|
273
|
+
const active = Boolean(metadata.activation && metadata.activation.active === true);
|
|
274
|
+
const nextActions = [];
|
|
275
|
+
|
|
276
|
+
if (!attached || !sourceKnown) {
|
|
277
|
+
nextActions.push('attach');
|
|
278
|
+
}
|
|
279
|
+
if (!workspacePath || !hydrated) {
|
|
280
|
+
nextActions.push('hydrate');
|
|
281
|
+
}
|
|
282
|
+
if (workspacePath && hydrated && !active) {
|
|
283
|
+
nextActions.push('activate');
|
|
284
|
+
}
|
|
285
|
+
if (engineeringProject.dirty_state === true) {
|
|
286
|
+
nextActions.push('review_local_changes');
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return Array.from(new Set(nextActions));
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function buildEngineeringPreviewSummary(graph = {}) {
|
|
293
|
+
const bundle = graph.bundle || {};
|
|
294
|
+
const engineeringProject = graph.engineering_project || {};
|
|
295
|
+
const metadata = engineeringProject.metadata && typeof engineeringProject.metadata === 'object'
|
|
296
|
+
? engineeringProject.metadata
|
|
297
|
+
: {};
|
|
298
|
+
const attached = Boolean(
|
|
299
|
+
engineeringProject.engineering_project_id
|
|
300
|
+
|| engineeringProject.repo_url
|
|
301
|
+
|| engineeringProject.project_key
|
|
302
|
+
|| engineeringProject.project_name
|
|
303
|
+
);
|
|
304
|
+
const sourceKnown = Boolean(normalizeString(engineeringProject.repo_url));
|
|
305
|
+
const workspacePath = normalizeString(engineeringProject.workspace_path) || null;
|
|
306
|
+
const hydrated = Boolean(metadata.hydration && metadata.hydration.status === 'ready');
|
|
307
|
+
const active = Boolean(metadata.activation && metadata.activation.active === true);
|
|
308
|
+
const readinessReasonCodes = buildEngineeringReadinessReasonCodes(graph);
|
|
309
|
+
|
|
222
310
|
return {
|
|
223
311
|
app_id: bundle.app_id || null,
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
312
|
+
appKey: bundle.app_key || null,
|
|
313
|
+
appName: bundle.app_name || null,
|
|
314
|
+
engineeringProjectId: engineeringProject.engineering_project_id || null,
|
|
315
|
+
projectName: engineeringProject.project_name || null,
|
|
316
|
+
projectKey: engineeringProject.project_key || null,
|
|
317
|
+
repoUrl: engineeringProject.repo_url || null,
|
|
318
|
+
provider: engineeringProject.repo_provider || null,
|
|
319
|
+
branch: engineeringProject.current_branch || engineeringProject.default_branch || null,
|
|
320
|
+
codeVersion: engineeringProject.code_version || null,
|
|
321
|
+
workspacePath,
|
|
322
|
+
dirtyState: engineeringProject.dirty_state === true,
|
|
323
|
+
attached,
|
|
324
|
+
hydrated,
|
|
325
|
+
active,
|
|
326
|
+
sourceKnown,
|
|
327
|
+
projectionReady: Boolean(workspacePath),
|
|
328
|
+
readinessReasonCodes,
|
|
329
|
+
nextActions: buildEngineeringPreviewNextActions(graph)
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function buildEngineeringSummary(graph = {}) {
|
|
334
|
+
const preview = buildEngineeringPreviewSummary(graph);
|
|
335
|
+
return {
|
|
336
|
+
app_id: preview.app_id,
|
|
337
|
+
app_name: preview.appName,
|
|
338
|
+
engineering_project_id: preview.engineeringProjectId,
|
|
339
|
+
project_name: preview.projectName,
|
|
340
|
+
repo_url: preview.repoUrl,
|
|
341
|
+
current_branch: preview.branch,
|
|
342
|
+
workspace_path: preview.workspacePath,
|
|
343
|
+
code_version: preview.codeVersion,
|
|
344
|
+
dirty_state: preview.dirtyState,
|
|
345
|
+
hydrated: preview.hydrated,
|
|
346
|
+
active: preview.active,
|
|
347
|
+
sourceKnown: preview.sourceKnown,
|
|
348
|
+
projectionReady: preview.projectionReady,
|
|
349
|
+
readinessReasonCodes: preview.readinessReasonCodes,
|
|
350
|
+
nextActions: preview.nextActions
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function buildEngineeringOwnershipRelation(graph = {}, currentDevice = null) {
|
|
355
|
+
const bundle = graph.bundle || {};
|
|
356
|
+
const engineeringProject = graph.engineering_project || {};
|
|
357
|
+
const metadata = engineeringProject.metadata && typeof engineeringProject.metadata === 'object'
|
|
358
|
+
? engineeringProject.metadata
|
|
359
|
+
: {};
|
|
360
|
+
const ownership = metadata.ownership && typeof metadata.ownership === 'object'
|
|
361
|
+
? metadata.ownership
|
|
362
|
+
: {};
|
|
363
|
+
const sharedPolicy = normalizeString(ownership.shared_policy || ownership.sharedPolicy) || null;
|
|
364
|
+
const explicitOwnershipType = normalizeString(ownership.ownership_type || ownership.ownershipType).toLowerCase();
|
|
365
|
+
const workspacePath = normalizeString(engineeringProject.workspace_path) || null;
|
|
366
|
+
let ownershipType = 'unresolved';
|
|
367
|
+
|
|
368
|
+
if (['local', 'shared', 'unresolved'].includes(explicitOwnershipType)) {
|
|
369
|
+
ownershipType = explicitOwnershipType;
|
|
370
|
+
} else if (sharedPolicy) {
|
|
371
|
+
ownershipType = 'shared';
|
|
372
|
+
} else if (workspacePath) {
|
|
373
|
+
ownershipType = 'local';
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
return {
|
|
377
|
+
appKey: bundle.app_key || null,
|
|
378
|
+
workspaceId: normalizeString(bundle.workspace_id || ownership.workspace_id || ownership.workspaceId) || null,
|
|
379
|
+
userId: normalizeString(ownership.user_id || ownership.userId) || null,
|
|
380
|
+
deviceId: normalizeString(ownership.device_id || ownership.deviceId) || (ownershipType === 'local' && currentDevice ? currentDevice.device_id : null),
|
|
381
|
+
ownershipType,
|
|
382
|
+
sharedPolicy
|
|
234
383
|
};
|
|
235
384
|
}
|
|
236
385
|
|
|
386
|
+
function buildEngineeringActionSteps(actionMode, graph = {}) {
|
|
387
|
+
const preview = buildEngineeringPreviewSummary(graph);
|
|
388
|
+
const attachReady = preview.attached && preview.sourceKnown;
|
|
389
|
+
const hydrateReady = preview.projectionReady && preview.hydrated;
|
|
390
|
+
const activateReady = preview.active;
|
|
391
|
+
const steps = [{
|
|
392
|
+
key: 'register',
|
|
393
|
+
status: 'done',
|
|
394
|
+
detail: 'App bundle is registered in SCE.'
|
|
395
|
+
}];
|
|
396
|
+
|
|
397
|
+
if (attachReady) {
|
|
398
|
+
steps.push({
|
|
399
|
+
key: 'attach',
|
|
400
|
+
status: 'done',
|
|
401
|
+
detail: 'Engineering source metadata is attached.'
|
|
402
|
+
});
|
|
403
|
+
} else {
|
|
404
|
+
steps.push({
|
|
405
|
+
key: 'attach',
|
|
406
|
+
status: 'pending',
|
|
407
|
+
reasonCode: ENGINEERING_PREVIEW_REASON_CODES.SOURCE_MISSING,
|
|
408
|
+
detail: 'Attach engineering source metadata before continuing.'
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
if (!attachReady) {
|
|
413
|
+
steps.push({
|
|
414
|
+
key: 'hydrate',
|
|
415
|
+
status: 'skipped',
|
|
416
|
+
reasonCode: ENGINEERING_PREVIEW_REASON_CODES.SOURCE_MISSING,
|
|
417
|
+
detail: 'Hydration is blocked until engineering source metadata is attached.'
|
|
418
|
+
});
|
|
419
|
+
} else if (!preview.projectionReady) {
|
|
420
|
+
steps.push({
|
|
421
|
+
key: 'hydrate',
|
|
422
|
+
status: 'pending',
|
|
423
|
+
reasonCode: ENGINEERING_PREVIEW_REASON_CODES.PROJECTION_MISSING,
|
|
424
|
+
detail: 'Create or bind a local engineering workspace before hydration.'
|
|
425
|
+
});
|
|
426
|
+
} else if (!preview.hydrated) {
|
|
427
|
+
steps.push({
|
|
428
|
+
key: 'hydrate',
|
|
429
|
+
status: 'pending',
|
|
430
|
+
reasonCode: ENGINEERING_PREVIEW_REASON_CODES.HYDRATE_REQUIRED,
|
|
431
|
+
detail: 'Engineering workspace exists but is not hydrated yet.'
|
|
432
|
+
});
|
|
433
|
+
} else {
|
|
434
|
+
steps.push({
|
|
435
|
+
key: 'hydrate',
|
|
436
|
+
status: 'done',
|
|
437
|
+
detail: 'Engineering workspace is hydrated.'
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
if (actionMode === 'import' && hydrateReady && !activateReady) {
|
|
442
|
+
steps.push({
|
|
443
|
+
key: 'activate',
|
|
444
|
+
status: 'skipped',
|
|
445
|
+
detail: 'Import completes without activation in phase-1.'
|
|
446
|
+
});
|
|
447
|
+
return steps;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
if (!attachReady) {
|
|
451
|
+
steps.push({
|
|
452
|
+
key: 'activate',
|
|
453
|
+
status: 'skipped',
|
|
454
|
+
reasonCode: ENGINEERING_PREVIEW_REASON_CODES.SOURCE_MISSING,
|
|
455
|
+
detail: 'Activation is blocked until engineering source metadata is attached.'
|
|
456
|
+
});
|
|
457
|
+
} else if (!preview.projectionReady) {
|
|
458
|
+
steps.push({
|
|
459
|
+
key: 'activate',
|
|
460
|
+
status: 'skipped',
|
|
461
|
+
reasonCode: ENGINEERING_PREVIEW_REASON_CODES.PROJECTION_MISSING,
|
|
462
|
+
detail: 'Activation is blocked until a local engineering workspace is available.'
|
|
463
|
+
});
|
|
464
|
+
} else if (!preview.hydrated) {
|
|
465
|
+
steps.push({
|
|
466
|
+
key: 'activate',
|
|
467
|
+
status: 'skipped',
|
|
468
|
+
reasonCode: ENGINEERING_PREVIEW_REASON_CODES.HYDRATE_REQUIRED,
|
|
469
|
+
detail: 'Activation is blocked until hydration completes.'
|
|
470
|
+
});
|
|
471
|
+
} else if (!preview.active) {
|
|
472
|
+
steps.push({
|
|
473
|
+
key: 'activate',
|
|
474
|
+
status: 'pending',
|
|
475
|
+
reasonCode: ENGINEERING_PREVIEW_REASON_CODES.ACTIVATE_REQUIRED,
|
|
476
|
+
detail: 'Engineering workspace is ready but not active.'
|
|
477
|
+
});
|
|
478
|
+
} else {
|
|
479
|
+
steps.push({
|
|
480
|
+
key: 'activate',
|
|
481
|
+
status: 'done',
|
|
482
|
+
detail: 'Engineering workspace is active.'
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
return steps;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
function buildEngineeringActionSuccess(actionMode, steps = []) {
|
|
490
|
+
if (actionMode === 'import') {
|
|
491
|
+
return ['register', 'attach', 'hydrate'].every((key) => steps.some((step) => step.key === key && step.status === 'done'));
|
|
492
|
+
}
|
|
493
|
+
return ['register', 'attach', 'hydrate', 'activate'].every((key) => steps.some((step) => step.key === key && step.status === 'done'));
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
async function runAppEngineeringActionEnvelopeCommand(actionMode, options = {}, dependencies = {}) {
|
|
497
|
+
const appRef = normalizeString(options.app);
|
|
498
|
+
if (!appRef) {
|
|
499
|
+
throw new Error('--app is required');
|
|
500
|
+
}
|
|
501
|
+
const { graph } = await requireAppGraph(appRef, dependencies);
|
|
502
|
+
const preview = buildEngineeringPreviewSummary(graph);
|
|
503
|
+
const steps = buildEngineeringActionSteps(actionMode, graph);
|
|
504
|
+
const payload = {
|
|
505
|
+
mode: actionMode,
|
|
506
|
+
generated_at: new Date().toISOString(),
|
|
507
|
+
query: {
|
|
508
|
+
app: appRef
|
|
509
|
+
},
|
|
510
|
+
success: buildEngineeringActionSuccess(actionMode, steps),
|
|
511
|
+
summary: preview,
|
|
512
|
+
preview,
|
|
513
|
+
steps,
|
|
514
|
+
bundle: graph.bundle,
|
|
515
|
+
engineering_project: graph.engineering_project,
|
|
516
|
+
scene_bindings: graph.scene_bindings || []
|
|
517
|
+
};
|
|
518
|
+
printPayload(payload, options, `App Engineering ${actionMode === 'open' ? 'Open' : 'Import'}`);
|
|
519
|
+
return payload;
|
|
520
|
+
}
|
|
521
|
+
|
|
237
522
|
function deriveEngineeringProjectId(bundle = {}) {
|
|
238
523
|
const appKey = normalizeString(bundle.app_key);
|
|
239
524
|
if (appKey) {
|
|
@@ -560,13 +845,15 @@ async function runAppEngineeringShowCommand(options = {}, dependencies = {}) {
|
|
|
560
845
|
throw new Error('--app is required');
|
|
561
846
|
}
|
|
562
847
|
const { graph } = await requireAppGraph(appRef, dependencies);
|
|
848
|
+
const preview = buildEngineeringPreviewSummary(graph);
|
|
563
849
|
const payload = {
|
|
564
850
|
mode: 'app-engineering-show',
|
|
565
851
|
generated_at: new Date().toISOString(),
|
|
566
852
|
query: {
|
|
567
853
|
app: appRef
|
|
568
854
|
},
|
|
569
|
-
summary:
|
|
855
|
+
summary: preview,
|
|
856
|
+
preview,
|
|
570
857
|
bundle: graph.bundle,
|
|
571
858
|
engineering_project: graph.engineering_project,
|
|
572
859
|
scene_bindings: graph.scene_bindings || []
|
|
@@ -575,6 +862,67 @@ async function runAppEngineeringShowCommand(options = {}, dependencies = {}) {
|
|
|
575
862
|
return payload;
|
|
576
863
|
}
|
|
577
864
|
|
|
865
|
+
async function runAppEngineeringPreviewCommand(options = {}, dependencies = {}) {
|
|
866
|
+
const appRef = normalizeString(options.app);
|
|
867
|
+
if (!appRef) {
|
|
868
|
+
throw new Error('--app is required');
|
|
869
|
+
}
|
|
870
|
+
const { graph } = await requireAppGraph(appRef, dependencies);
|
|
871
|
+
const preview = buildEngineeringPreviewSummary(graph);
|
|
872
|
+
const payload = {
|
|
873
|
+
mode: 'app-engineering-preview',
|
|
874
|
+
generated_at: new Date().toISOString(),
|
|
875
|
+
query: {
|
|
876
|
+
app: appRef
|
|
877
|
+
},
|
|
878
|
+
summary: preview,
|
|
879
|
+
preview,
|
|
880
|
+
bundle: graph.bundle,
|
|
881
|
+
engineering_project: graph.engineering_project,
|
|
882
|
+
scene_bindings: graph.scene_bindings || []
|
|
883
|
+
};
|
|
884
|
+
printPayload(payload, options, 'App Engineering Preview');
|
|
885
|
+
return payload;
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
async function runAppEngineeringOwnershipCommand(options = {}, dependencies = {}) {
|
|
889
|
+
const appRef = normalizeString(options.app);
|
|
890
|
+
if (!appRef) {
|
|
891
|
+
throw new Error('--app is required');
|
|
892
|
+
}
|
|
893
|
+
const projectPath = dependencies.projectPath || process.cwd();
|
|
894
|
+
const fileSystem = dependencies.fileSystem || fs;
|
|
895
|
+
const { graph } = await requireAppGraph(appRef, dependencies);
|
|
896
|
+
const currentDevice = await getCurrentDeviceProfile(projectPath, {
|
|
897
|
+
fileSystem,
|
|
898
|
+
persistIfMissing: false
|
|
899
|
+
});
|
|
900
|
+
const ownership = buildEngineeringOwnershipRelation(graph, currentDevice);
|
|
901
|
+
const payload = {
|
|
902
|
+
mode: 'app-engineering-ownership',
|
|
903
|
+
generated_at: new Date().toISOString(),
|
|
904
|
+
query: {
|
|
905
|
+
app: appRef
|
|
906
|
+
},
|
|
907
|
+
summary: ownership,
|
|
908
|
+
ownership,
|
|
909
|
+
current_device: currentDevice,
|
|
910
|
+
bundle: graph.bundle,
|
|
911
|
+
engineering_project: graph.engineering_project,
|
|
912
|
+
scene_bindings: graph.scene_bindings || []
|
|
913
|
+
};
|
|
914
|
+
printPayload(payload, options, 'App Engineering Ownership');
|
|
915
|
+
return payload;
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
async function runAppEngineeringOpenCommand(options = {}, dependencies = {}) {
|
|
919
|
+
return runAppEngineeringActionEnvelopeCommand('open', options, dependencies);
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
async function runAppEngineeringImportCommand(options = {}, dependencies = {}) {
|
|
923
|
+
return runAppEngineeringActionEnvelopeCommand('import', options, dependencies);
|
|
924
|
+
}
|
|
925
|
+
|
|
578
926
|
async function runAppEngineeringAttachCommand(options = {}, dependencies = {}) {
|
|
579
927
|
const appRef = normalizeString(options.app);
|
|
580
928
|
if (!appRef) {
|
|
@@ -675,6 +1023,42 @@ async function runAppEngineeringHydrateCommand(options = {}, dependencies = {})
|
|
|
675
1023
|
return payload;
|
|
676
1024
|
}
|
|
677
1025
|
|
|
1026
|
+
async function runAppEngineeringScaffoldCommand(options = {}, dependencies = {}) {
|
|
1027
|
+
const appRef = normalizeString(options.app);
|
|
1028
|
+
if (!appRef) {
|
|
1029
|
+
throw new Error('--app is required');
|
|
1030
|
+
}
|
|
1031
|
+
await ensureAuthorized('app:engineering:scaffold', options, dependencies);
|
|
1032
|
+
const { graph } = await requireAppGraph(appRef, dependencies);
|
|
1033
|
+
const engineeringProject = graph.engineering_project || {};
|
|
1034
|
+
const workspacePath = normalizeString(options.workspacePath) || normalizeString(engineeringProject.workspace_path);
|
|
1035
|
+
if (!workspacePath) {
|
|
1036
|
+
throw new Error('engineering workspace_path is not set; run app engineering hydrate first or pass --workspace-path');
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
const result = await scaffoldEngineeringWorkspace({
|
|
1040
|
+
workspacePath,
|
|
1041
|
+
overwritePolicy: options.overwritePolicy,
|
|
1042
|
+
fileSystem: dependencies.fileSystem || fs,
|
|
1043
|
+
templateRoot: dependencies.templateRoot
|
|
1044
|
+
});
|
|
1045
|
+
const payload = {
|
|
1046
|
+
mode: 'app-engineering-scaffold',
|
|
1047
|
+
generated_at: new Date().toISOString(),
|
|
1048
|
+
query: {
|
|
1049
|
+
app: appRef
|
|
1050
|
+
},
|
|
1051
|
+
success: result.failedDirectoryCount === 0 && result.failedFileCount === 0,
|
|
1052
|
+
summary: result,
|
|
1053
|
+
result,
|
|
1054
|
+
bundle: graph.bundle,
|
|
1055
|
+
engineering_project: graph.engineering_project,
|
|
1056
|
+
scene_bindings: graph.scene_bindings || []
|
|
1057
|
+
};
|
|
1058
|
+
printPayload(payload, options, 'App Engineering Scaffold');
|
|
1059
|
+
return payload;
|
|
1060
|
+
}
|
|
1061
|
+
|
|
678
1062
|
async function runAppEngineeringActivateCommand(options = {}, dependencies = {}) {
|
|
679
1063
|
const appRef = normalizeString(options.app);
|
|
680
1064
|
if (!appRef) {
|
|
@@ -1246,9 +1630,37 @@ function registerAppCommands(program) {
|
|
|
1246
1630
|
.command('engineering')
|
|
1247
1631
|
.description('Manage engineering project projection for one app bundle');
|
|
1248
1632
|
|
|
1633
|
+
engineering
|
|
1634
|
+
.command('preview')
|
|
1635
|
+
.description('Preview engineering readiness for one app bundle')
|
|
1636
|
+
.requiredOption('--app <app-id-or-key>', 'App id or app key')
|
|
1637
|
+
.option('--json', 'Print machine-readable JSON output')
|
|
1638
|
+
.action((options) => safeRun(runAppEngineeringPreviewCommand, options, 'app engineering preview'));
|
|
1639
|
+
|
|
1640
|
+
engineering
|
|
1641
|
+
.command('ownership')
|
|
1642
|
+
.description('Show the read-only engineering ownership relation for one app bundle')
|
|
1643
|
+
.requiredOption('--app <app-id-or-key>', 'App id or app key')
|
|
1644
|
+
.option('--json', 'Print machine-readable JSON output')
|
|
1645
|
+
.action((options) => safeRun(runAppEngineeringOwnershipCommand, options, 'app engineering ownership'));
|
|
1646
|
+
|
|
1647
|
+
engineering
|
|
1648
|
+
.command('open')
|
|
1649
|
+
.description('Return the canonical engineering open result envelope')
|
|
1650
|
+
.requiredOption('--app <app-id-or-key>', 'App id or app key')
|
|
1651
|
+
.option('--json', 'Print machine-readable JSON output')
|
|
1652
|
+
.action((options) => safeRun(runAppEngineeringOpenCommand, options, 'app engineering open'));
|
|
1653
|
+
|
|
1654
|
+
engineering
|
|
1655
|
+
.command('import')
|
|
1656
|
+
.description('Return the canonical engineering import result envelope')
|
|
1657
|
+
.requiredOption('--app <app-id-or-key>', 'App id or app key')
|
|
1658
|
+
.option('--json', 'Print machine-readable JSON output')
|
|
1659
|
+
.action((options) => safeRun(runAppEngineeringImportCommand, options, 'app engineering import'));
|
|
1660
|
+
|
|
1249
1661
|
engineering
|
|
1250
1662
|
.command('show')
|
|
1251
|
-
.description('Show engineering projection for one app bundle')
|
|
1663
|
+
.description('Show engineering projection for one app bundle (compatibility alias of preview)')
|
|
1252
1664
|
.requiredOption('--app <app-id-or-key>', 'App id or app key')
|
|
1253
1665
|
.option('--json', 'Print machine-readable JSON output')
|
|
1254
1666
|
.action((options) => safeRun(runAppEngineeringShowCommand, options, 'app engineering show'));
|
|
@@ -1281,6 +1693,18 @@ function registerAppCommands(program) {
|
|
|
1281
1693
|
.option('--json', 'Print machine-readable JSON output')
|
|
1282
1694
|
.action((options) => safeRun(runAppEngineeringHydrateCommand, options, 'app engineering hydrate'));
|
|
1283
1695
|
|
|
1696
|
+
engineering
|
|
1697
|
+
.command('scaffold')
|
|
1698
|
+
.description('Scaffold SCE baseline files into the engineering workspace')
|
|
1699
|
+
.requiredOption('--app <app-id-or-key>', 'App id or app key')
|
|
1700
|
+
.option('--workspace-path <path>', 'Override workspace path for scaffold')
|
|
1701
|
+
.option('--overwrite-policy <policy>', 'Overwrite policy: never, missing-only, explicit', 'missing-only')
|
|
1702
|
+
.option('--auth-lease <lease-id>', 'Write authorization lease id')
|
|
1703
|
+
.option('--auth-password <password>', 'Inline auth password if policy allows')
|
|
1704
|
+
.option('--actor <actor>', 'Audit actor override')
|
|
1705
|
+
.option('--json', 'Print machine-readable JSON output')
|
|
1706
|
+
.action((options) => safeRun(runAppEngineeringScaffoldCommand, options, 'app engineering scaffold'));
|
|
1707
|
+
|
|
1284
1708
|
engineering
|
|
1285
1709
|
.command('activate')
|
|
1286
1710
|
.description('Activate engineering workspace for one app bundle')
|
|
@@ -1311,9 +1735,14 @@ module.exports = {
|
|
|
1311
1735
|
runAppRuntimeActivateCommand,
|
|
1312
1736
|
runAppRuntimeUninstallCommand,
|
|
1313
1737
|
runAppInstallStateListCommand,
|
|
1738
|
+
runAppEngineeringPreviewCommand,
|
|
1739
|
+
runAppEngineeringOwnershipCommand,
|
|
1740
|
+
runAppEngineeringOpenCommand,
|
|
1741
|
+
runAppEngineeringImportCommand,
|
|
1314
1742
|
runAppEngineeringShowCommand,
|
|
1315
1743
|
runAppEngineeringAttachCommand,
|
|
1316
1744
|
runAppEngineeringHydrateCommand,
|
|
1745
|
+
runAppEngineeringScaffoldCommand,
|
|
1317
1746
|
runAppEngineeringActivateCommand,
|
|
1318
1747
|
registerAppCommands
|
|
1319
1748
|
};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const { buildProjectPortfolioProjection } = require('../project/portfolio-projection-service');
|
|
3
|
+
const { buildProjectSupervisionProjection } = require('../project/supervision-projection-service');
|
|
4
|
+
const { resolveProjectTarget } = require('../project/target-resolution-service');
|
|
5
|
+
|
|
6
|
+
async function runProjectPortfolioShowCommand(options = {}, dependencies = {}) {
|
|
7
|
+
const payload = await buildProjectPortfolioProjection(options, dependencies);
|
|
8
|
+
if (options.json) {
|
|
9
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
10
|
+
} else {
|
|
11
|
+
console.log(chalk.blue('Project Portfolio'));
|
|
12
|
+
console.log(` Active Project: ${payload.activeProjectId || 'none'}`);
|
|
13
|
+
console.log(` Visible Projects: ${payload.projects.length}`);
|
|
14
|
+
}
|
|
15
|
+
return payload;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async function runProjectTargetResolveCommand(options = {}, dependencies = {}) {
|
|
19
|
+
const payload = await resolveProjectTarget(options, dependencies);
|
|
20
|
+
if (options.json) {
|
|
21
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
22
|
+
} else {
|
|
23
|
+
console.log(chalk.blue('Project Target Resolve'));
|
|
24
|
+
console.log(` Status: ${payload.status}`);
|
|
25
|
+
console.log(` Resolved Project: ${payload.resolvedProjectId || 'none'}`);
|
|
26
|
+
}
|
|
27
|
+
return payload;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function runProjectSupervisionShowCommand(options = {}, dependencies = {}) {
|
|
31
|
+
const payload = await buildProjectSupervisionProjection(options, dependencies);
|
|
32
|
+
if (options.json) {
|
|
33
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
34
|
+
} else {
|
|
35
|
+
console.log(chalk.blue('Project Supervision'));
|
|
36
|
+
console.log(` Project: ${payload.projectId}`);
|
|
37
|
+
console.log(` Blocked: ${payload.summary.blockedCount}`);
|
|
38
|
+
console.log(` Handoff: ${payload.summary.handoffCount}`);
|
|
39
|
+
console.log(` Risk: ${payload.summary.riskCount}`);
|
|
40
|
+
}
|
|
41
|
+
return payload;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function safeRun(handler, options = {}, context = 'project command') {
|
|
45
|
+
Promise.resolve(handler(options))
|
|
46
|
+
.catch((error) => {
|
|
47
|
+
if (options.json) {
|
|
48
|
+
console.log(JSON.stringify({ success: false, error: error.message }, null, 2));
|
|
49
|
+
} else {
|
|
50
|
+
console.error(chalk.red(`${context} failed:`), error.message);
|
|
51
|
+
}
|
|
52
|
+
process.exitCode = 1;
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function registerProjectCommands(program) {
|
|
57
|
+
const project = program
|
|
58
|
+
.command('project')
|
|
59
|
+
.description('Inspect multi-project portfolio and routing projections');
|
|
60
|
+
|
|
61
|
+
const portfolio = project
|
|
62
|
+
.command('portfolio')
|
|
63
|
+
.description('Inspect the caller-visible project portfolio');
|
|
64
|
+
|
|
65
|
+
portfolio
|
|
66
|
+
.command('show')
|
|
67
|
+
.description('Show the canonical project portfolio projection')
|
|
68
|
+
.option('--workspace <name>', 'Resolve caller context against one registered workspace')
|
|
69
|
+
.option('--json', 'Print machine-readable JSON output')
|
|
70
|
+
.action((options) => safeRun(runProjectPortfolioShowCommand, options, 'project portfolio show'));
|
|
71
|
+
|
|
72
|
+
const target = project
|
|
73
|
+
.command('target')
|
|
74
|
+
.description('Resolve target project against the caller-visible portfolio');
|
|
75
|
+
|
|
76
|
+
target
|
|
77
|
+
.command('resolve')
|
|
78
|
+
.description('Resolve one project target without mutating active workspace selection')
|
|
79
|
+
.option('--request <text>', 'Routing request text')
|
|
80
|
+
.option('--current-project <id>', 'Caller asserted current project id')
|
|
81
|
+
.option('--workspace <name>', 'Resolve caller context against one registered workspace')
|
|
82
|
+
.option('--device <id>', 'Opaque caller device id')
|
|
83
|
+
.option('--tool-instance-id <id>', 'Opaque caller tool instance id')
|
|
84
|
+
.option('--json', 'Print machine-readable JSON output')
|
|
85
|
+
.action((options) => safeRun(runProjectTargetResolveCommand, options, 'project target resolve'));
|
|
86
|
+
|
|
87
|
+
const supervision = project
|
|
88
|
+
.command('supervision')
|
|
89
|
+
.description('Inspect project-scoped supervision projection');
|
|
90
|
+
|
|
91
|
+
supervision
|
|
92
|
+
.command('show')
|
|
93
|
+
.description('Show project-scoped supervision summary and drillback items')
|
|
94
|
+
.requiredOption('--project <id>', 'Visible project id')
|
|
95
|
+
.option('--cursor <cursor>', 'Best-effort incremental checkpoint; full snapshot remains supported')
|
|
96
|
+
.option('--json', 'Print machine-readable JSON output')
|
|
97
|
+
.action((options) => safeRun(runProjectSupervisionShowCommand, options, 'project supervision show'));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
module.exports = {
|
|
101
|
+
runProjectPortfolioShowCommand,
|
|
102
|
+
runProjectTargetResolveCommand,
|
|
103
|
+
runProjectSupervisionShowCommand,
|
|
104
|
+
registerProjectCommands
|
|
105
|
+
};
|
package/lib/commands/scene.js
CHANGED
|
@@ -11,6 +11,7 @@ const { executeInstallPlan } = require('../app/install-apply-runner');
|
|
|
11
11
|
const { runAppRuntimeInstallCommand, runAppRuntimeActivateCommand, runAppRuntimeUninstallCommand } = require('./app');
|
|
12
12
|
const { loadDeviceOverride } = require('../device/device-override-store');
|
|
13
13
|
const { getCurrentDeviceProfile } = require('../device/current-device');
|
|
14
|
+
const { runSceneDeliveryShowCommand } = require('../scene/delivery-projection-service');
|
|
14
15
|
|
|
15
16
|
const semver = require('semver');
|
|
16
17
|
const SceneLoader = require('../scene-runtime/scene-loader');
|
|
@@ -255,6 +256,20 @@ function registerSceneCommands(program) {
|
|
|
255
256
|
await runSceneWorkspaceCommand(runSceneWorkspaceApplyCommand, options, 'scene workspace apply');
|
|
256
257
|
});
|
|
257
258
|
|
|
259
|
+
const deliveryCmd = sceneCmd
|
|
260
|
+
.command('delivery')
|
|
261
|
+
.description('Inspect scene delivery projection envelopes');
|
|
262
|
+
|
|
263
|
+
deliveryCmd
|
|
264
|
+
.command('show')
|
|
265
|
+
.description('Show delivery projection for one scene and optional spec')
|
|
266
|
+
.requiredOption('--scene <id>', 'Scene id')
|
|
267
|
+
.option('--spec <id>', 'Optional spec id filter')
|
|
268
|
+
.option('--json', 'Print machine-readable JSON output')
|
|
269
|
+
.action(async (options) => {
|
|
270
|
+
await runSceneWorkspaceCommand(runSceneDeliveryShowCommand, options, 'scene delivery show');
|
|
271
|
+
});
|
|
272
|
+
|
|
258
273
|
sceneCmd
|
|
259
274
|
.command('validate')
|
|
260
275
|
.description('Validate scene manifest from spec or file path')
|
|
@@ -16777,6 +16792,7 @@ module.exports = {
|
|
|
16777
16792
|
validateSceneContributeOptions,
|
|
16778
16793
|
runSceneContributeCommand,
|
|
16779
16794
|
printSceneContributeSummary,
|
|
16795
|
+
runSceneDeliveryShowCommand,
|
|
16780
16796
|
runSceneWorkspaceListCommand,
|
|
16781
16797
|
runSceneWorkspaceShowCommand,
|
|
16782
16798
|
runSceneWorkspaceApplyCommand,
|