@hongmaple0820/scale-engine 0.40.0 → 0.40.2
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/api/cli.js +421 -26
- package/dist/api/cli.js.map +1 -1
- package/dist/api/doctor.js +1 -1
- package/dist/api/doctor.js.map +1 -1
- package/dist/bootstrap/DependencyBootstrap.d.ts +2 -0
- package/dist/bootstrap/DependencyBootstrap.js +250 -39
- package/dist/bootstrap/DependencyBootstrap.js.map +1 -1
- package/dist/bootstrap/DependencyBootstrapRenderer.js +2 -4
- package/dist/bootstrap/DependencyBootstrapRenderer.js.map +1 -1
- package/dist/capabilities/InstalledSkillsIntegration.js +29 -9
- package/dist/capabilities/InstalledSkillsIntegration.js.map +1 -1
- package/dist/context/ContextBudget.js +2 -2
- package/dist/core/GbrainRuntime.d.ts +25 -0
- package/dist/core/GbrainRuntime.js +270 -0
- package/dist/core/GbrainRuntime.js.map +1 -0
- package/dist/env/EnvironmentDoctor.js +221 -5
- package/dist/env/EnvironmentDoctor.js.map +1 -1
- package/dist/memory/MemoryProviders.js +38 -91
- package/dist/memory/MemoryProviders.js.map +1 -1
- package/dist/runtime/ModelUsageLedger.d.ts +53 -2
- package/dist/runtime/ModelUsageLedger.js +243 -39
- package/dist/runtime/ModelUsageLedger.js.map +1 -1
- package/dist/setup/SetupVerification.d.ts +42 -0
- package/dist/setup/SetupVerification.js +180 -0
- package/dist/setup/SetupVerification.js.map +1 -0
- package/dist/setup/SetupWizard.d.ts +3 -0
- package/dist/setup/SetupWizard.js +79 -19
- package/dist/setup/SetupWizard.js.map +1 -1
- package/dist/skills/SkillDoctor.js +2 -2
- package/dist/skills/SkillDoctor.js.map +1 -1
- package/dist/skills/SkillRepository.js +2 -2
- package/dist/skills/SkillRepository.js.map +1 -1
- package/dist/tools/ToolCapabilityRegistry.js +12 -2
- package/dist/tools/ToolCapabilityRegistry.js.map +1 -1
- package/dist/workflow/VerificationProfile.js +1 -1
- package/dist/workflow/VerificationProfile.js.map +1 -1
- package/docs/CONTEXT_BUDGET.md +12 -2
- package/docs/GOVERNANCE_DASHBOARD.md +7 -0
- package/docs/THIRD_PARTY_SKILLS.md +12 -4
- package/docs/start/README.md +2 -2
- package/docs/start/quickstart.md +54 -44
- package/docs/start/workflow-upgrade.md +8 -1
- package/package.json +3 -2
- package/scripts/workflow/lib/gbrain-runtime.mjs +185 -0
- package/scripts/workflow/lib/report-output.mjs +107 -0
- package/scripts/workflow/provider-rehearsal.mjs +129 -48
- package/scripts/workflow/setup-smoke.mjs +142 -8
package/dist/api/cli.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// SCALE Engine — CLI 入口 (W6 完整实现)
|
|
3
3
|
// 所有 Hook 调用入口: session/gate/create/list/transition/context
|
|
4
4
|
import { defineCommand, runMain } from 'citty';
|
|
5
|
+
import { createInterface } from 'node:readline';
|
|
5
6
|
import { EventBus } from '../core/eventBus.js';
|
|
6
7
|
import { SQLiteArtifactStore } from '../artifact/sqliteStore.js';
|
|
7
8
|
import { FSM } from '../artifact/fsm.js';
|
|
@@ -32,6 +33,7 @@ import { quickStart, detectPlatform, governanceNextSteps } from './quickstart.js
|
|
|
32
33
|
import { bootstrapDependencies } from '../bootstrap/DependencyBootstrap.js';
|
|
33
34
|
import { renderDependencyBootstrapReport } from '../bootstrap/DependencyBootstrapRenderer.js';
|
|
34
35
|
import { runSetupWizard } from '../setup/SetupWizard.js';
|
|
36
|
+
import { verifySetup } from '../setup/SetupVerification.js';
|
|
35
37
|
import { normalizeLanguage, resolveCliLanguage } from '../i18n/Language.js';
|
|
36
38
|
import { SkillDiscovery } from '../skills/SkillDiscovery.js';
|
|
37
39
|
import { inspectRequiredWorkflowSkills, inspectWorkflowSkills } from '../skills/SkillDoctor.js';
|
|
@@ -74,7 +76,7 @@ import { doctorHtmlArtifacts, renderHtmlArtifact, resolveHtmlArtifactForOpen, se
|
|
|
74
76
|
import { renderGovernanceDashboard } from '../output/GovernanceDashboard.js';
|
|
75
77
|
import { cleanupWorkspaceLifecycle, inspectWorkspaceLifecycle, } from '../workflow/WorkspaceLifecycle.js';
|
|
76
78
|
import { inspectWorkspaceSafety } from '../workflow/WorkspaceSafety.js';
|
|
77
|
-
import { RuntimeEvidenceLedger, SessionLedger, createAiOsAdoption, createAiOsBenchmark, createAiOsDashboard, createAiOsDoctor, createAiOsMigration, createAiOsPlan, createAiOsRun, createAiOsStatus, doctorRuntimeEvidence, evaluateFinalReportReadiness, } from '../runtime/index.js';
|
|
79
|
+
import { ModelUsageLedger, RuntimeEvidenceLedger, SessionLedger, buildModelUsageInput, createAiOsAdoption, createAiOsBenchmark, createAiOsDashboard, createAiOsDoctor, createAiOsMigration, createAiOsPlan, createAiOsRun, createAiOsStatus, doctorRuntimeEvidence, evaluateFinalReportReadiness, } from '../runtime/index.js';
|
|
78
80
|
import { MemoryFabric, MemoryBrain, doctorMemoryFabric, renderContextPackMarkdown, renderMemoryLearningCandidateMarkdown, inspectMemoryProviders, recallMemoryProviders, settleMemoryLearning, useMemoryProvider, writeMemoryProvidersConfig, } from '../memory/index.js';
|
|
79
81
|
import { resolveWorkspaceTopology, workspaceTopologyPath, workspaceTopologyTemplate, } from '../workflow/WorkspaceTopology.js';
|
|
80
82
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
@@ -2816,7 +2818,6 @@ const bootstrap = defineCommand({
|
|
|
2816
2818
|
meta: { name: 'bootstrap', description: 'Bootstrap third-party workflow dependencies with explicit install intent' },
|
|
2817
2819
|
subCommands: { deps: bootstrapDepsCommand },
|
|
2818
2820
|
});
|
|
2819
|
-
// ============================================================================
|
|
2820
2821
|
const setup = defineCommand({
|
|
2821
2822
|
meta: { name: 'setup', description: 'Interactive SCALE setup for third-party skills, CLIs, memory, and knowledge providers' },
|
|
2822
2823
|
args: {
|
|
@@ -2827,6 +2828,7 @@ const setup = defineCommand({
|
|
|
2827
2828
|
include: { type: 'string', description: 'Additional dependency ids to include explicitly' },
|
|
2828
2829
|
apply: { type: 'boolean', default: false, description: 'Run install commands for ready dependencies' },
|
|
2829
2830
|
yes: { type: 'boolean', default: false, description: 'Confirm installation without prompting' },
|
|
2831
|
+
verify: { type: 'boolean', default: false, description: 'Verify governed setup and dependency readiness instead of running the setup wizard' },
|
|
2830
2832
|
interactive: { type: 'boolean', default: true, description: 'Prompt before installation when dependencies are ready' },
|
|
2831
2833
|
lang: { type: 'string', description: 'Output language zh/en. Defaults to zh, then SCALE_LANG, then .scale/config.yaml locale.' },
|
|
2832
2834
|
'memory-provider': { type: 'string', description: 'Switch memory provider during setup: gbrain, agentmemory, or scale-local' },
|
|
@@ -2839,15 +2841,30 @@ const setup = defineCommand({
|
|
|
2839
2841
|
async run({ args }) {
|
|
2840
2842
|
const projectDir = resolve(String(args.dir ?? PROJECT_DIR));
|
|
2841
2843
|
const lang = resolveCliLanguage({ lang: args.lang, projectDir, scaleDir: SCALE_DIR });
|
|
2842
|
-
const explicitPacks =
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2844
|
+
const { explicitPacks, recommendedPacks } = resolveSetupPacks(args);
|
|
2845
|
+
if (isTruthyFlag(args.verify)) {
|
|
2846
|
+
const verification = await verifySetup({
|
|
2847
|
+
projectDir,
|
|
2848
|
+
scaleDir: SCALE_DIR,
|
|
2849
|
+
packIds: explicitPacks.length > 0 ? uniqueStrings([...recommendedPacks, ...explicitPacks]) : recommendedPacks,
|
|
2850
|
+
includeIds: parseCommaList(args.include),
|
|
2851
|
+
});
|
|
2852
|
+
if (args.json) {
|
|
2853
|
+
console.log(JSON.stringify(verification, null, 2));
|
|
2854
|
+
}
|
|
2855
|
+
else {
|
|
2856
|
+
renderSetupVerifyReport(verification, lang);
|
|
2857
|
+
}
|
|
2858
|
+
if (!verification.ok)
|
|
2859
|
+
process.exitCode = 1;
|
|
2860
|
+
return;
|
|
2861
|
+
}
|
|
2846
2862
|
const report = await runSetupWizard({
|
|
2847
2863
|
projectDir,
|
|
2848
2864
|
scaleDir: SCALE_DIR,
|
|
2849
2865
|
packIds: explicitPacks.length > 0 ? uniqueStrings([...recommendedPacks, ...explicitPacks]) : recommendedPacks,
|
|
2850
2866
|
includeIds: parseCommaList(args.include),
|
|
2867
|
+
promptPacks: explicitPacks.length === 0 && recommendedPacks.length === 0 && !args.include,
|
|
2851
2868
|
apply: isTruthyFlag(args.apply),
|
|
2852
2869
|
yes: isTruthyFlag(args.yes),
|
|
2853
2870
|
interactive: isTruthyFlag(args.interactive) && !isTruthyFlag(args.json),
|
|
@@ -2859,34 +2876,95 @@ const setup = defineCommand({
|
|
|
2859
2876
|
allowExternalWrite: isTruthyFlag(args['allow-external-write']) ? true : undefined,
|
|
2860
2877
|
promptLanguage: isTruthyFlag(args.interactive) && !args.lang,
|
|
2861
2878
|
});
|
|
2879
|
+
if (!args.json) {
|
|
2880
|
+
console.log(lang === 'zh' ? '\nSCALE 交互式安装' : '\nSCALE Interactive Setup');
|
|
2881
|
+
console.log(lang === 'zh'
|
|
2882
|
+
? ` 已执行安装: ${report.applied ? '是' : '否'}`
|
|
2883
|
+
: ` Applied: ${report.applied}`);
|
|
2884
|
+
if (report.memoryProviderSwitch) {
|
|
2885
|
+
const switched = report.memoryProviderSwitch;
|
|
2886
|
+
console.log(lang === 'zh' ? ' 记忆供应商:' : ' Memory provider:');
|
|
2887
|
+
console.log(` provider=${switched.provider}; mode=${switched.mode}; config=${switched.path}`);
|
|
2888
|
+
console.log(` order=${switched.previousOrder.join(' -> ')} => ${switched.nextOrder.join(' -> ')}`);
|
|
2889
|
+
if (switched.providerStatus) {
|
|
2890
|
+
console.log(` status=${switched.providerStatus.available ? 'available' : 'not-ready'}; reason=${switched.providerStatus.reason}`);
|
|
2891
|
+
}
|
|
2892
|
+
for (const warning of switched.warnings)
|
|
2893
|
+
console.log(lang === 'zh' ? ` [警告] ${warning}` : ` [WARN] ${warning}`);
|
|
2894
|
+
}
|
|
2895
|
+
console.log(renderDependencyBootstrapReport(report.final, lang));
|
|
2896
|
+
if (!report.ok)
|
|
2897
|
+
process.exitCode = 1;
|
|
2898
|
+
return;
|
|
2899
|
+
}
|
|
2862
2900
|
if (args.json) {
|
|
2863
2901
|
console.log(JSON.stringify(report, null, 2));
|
|
2864
2902
|
if (!report.ok)
|
|
2865
2903
|
process.exitCode = 1;
|
|
2866
2904
|
return;
|
|
2867
2905
|
}
|
|
2868
|
-
console.log(lang === 'zh' ? '\nSCALE 交互式安装' : '\nSCALE Interactive Setup');
|
|
2869
|
-
console.log(lang === 'zh'
|
|
2870
|
-
? ` 已执行安装: ${report.applied ? '是' : '否'}`
|
|
2871
|
-
: ` Applied: ${report.applied}`);
|
|
2872
|
-
if (report.memoryProviderSwitch) {
|
|
2873
|
-
const switched = report.memoryProviderSwitch;
|
|
2874
|
-
console.log(lang === 'zh' ? ' 记忆供应商:' : ' Memory provider:');
|
|
2875
|
-
console.log(` provider=${switched.provider}; mode=${switched.mode}; config=${switched.path}`);
|
|
2876
|
-
console.log(` order=${switched.previousOrder.join(' -> ')} => ${switched.nextOrder.join(' -> ')}`);
|
|
2877
|
-
if (switched.providerStatus) {
|
|
2878
|
-
console.log(` status=${switched.providerStatus.available ? 'available' : 'not-ready'}; reason=${switched.providerStatus.reason}`);
|
|
2879
|
-
}
|
|
2880
|
-
for (const warning of switched.warnings)
|
|
2881
|
-
console.log(lang === 'zh' ? ` [警告] ${warning}` : ` [WARN] ${warning}`);
|
|
2882
|
-
}
|
|
2883
|
-
console.log(renderDependencyBootstrapReport(report.final, lang));
|
|
2884
|
-
if (!report.ok)
|
|
2885
|
-
process.exitCode = 1;
|
|
2886
2906
|
},
|
|
2887
2907
|
});
|
|
2888
2908
|
// config command — Configuration profile management
|
|
2889
2909
|
// ============================================================================
|
|
2910
|
+
function resolveSetupPacks(args) {
|
|
2911
|
+
const explicitPacks = parseCommaList(args.pack);
|
|
2912
|
+
const recommendedPacks = args.profile
|
|
2913
|
+
? getBootstrapPlanForProfile(String(args.profile), args['governance-pack'] ? String(args['governance-pack']) : undefined).packs
|
|
2914
|
+
: [];
|
|
2915
|
+
return { explicitPacks, recommendedPacks };
|
|
2916
|
+
}
|
|
2917
|
+
function renderSetupVerifyReport(report, lang) {
|
|
2918
|
+
if (lang === 'zh') {
|
|
2919
|
+
console.log('\nSCALE 安装验收');
|
|
2920
|
+
console.log(` 项目: ${report.projectDir}`);
|
|
2921
|
+
console.log(` 依赖包: ${report.packIds.join(', ') || 'full'}`);
|
|
2922
|
+
console.log(` 结论: ${report.ok ? '通过' : '未通过'}`);
|
|
2923
|
+
console.log(` 阻塞项: ${report.summary.blockingIssues.length}`);
|
|
2924
|
+
console.log(` 受管能力: ${report.summary.installedTools}/${report.summary.totalTools}`);
|
|
2925
|
+
console.log(` 记忆供应商: ${report.summary.availableMemoryProviders}`);
|
|
2926
|
+
console.log(` 代码图谱供应商: ${report.summary.availableCodeProviders}`);
|
|
2927
|
+
if (report.summary.blockingIssues.length > 0) {
|
|
2928
|
+
console.log(' 阻塞详情:');
|
|
2929
|
+
for (const issue of report.summary.blockingIssues)
|
|
2930
|
+
console.log(` - ${issue}`);
|
|
2931
|
+
}
|
|
2932
|
+
if (report.warnings.length > 0) {
|
|
2933
|
+
console.log(` 警告${report.warnings.length > 12 ? ` (显示前 12 条,共 ${report.warnings.length} 条)` : ''}:`);
|
|
2934
|
+
for (const warning of report.warnings.slice(0, 12))
|
|
2935
|
+
console.log(` - ${warning}`);
|
|
2936
|
+
}
|
|
2937
|
+
if (report.recommendations.length > 0) {
|
|
2938
|
+
console.log(' 下一步:');
|
|
2939
|
+
for (const command of report.recommendations.slice(0, 12))
|
|
2940
|
+
console.log(` ${command}`);
|
|
2941
|
+
}
|
|
2942
|
+
return;
|
|
2943
|
+
}
|
|
2944
|
+
console.log('\nSCALE Setup Verification');
|
|
2945
|
+
console.log(` Project: ${report.projectDir}`);
|
|
2946
|
+
console.log(` Packs: ${report.packIds.join(', ') || 'full'}`);
|
|
2947
|
+
console.log(` Result: ${report.ok ? 'passed' : 'failed'}`);
|
|
2948
|
+
console.log(` Blocking issues: ${report.summary.blockingIssues.length}`);
|
|
2949
|
+
console.log(` Governed capabilities: ${report.summary.installedTools}/${report.summary.totalTools}`);
|
|
2950
|
+
console.log(` Memory providers: ${report.summary.availableMemoryProviders}`);
|
|
2951
|
+
console.log(` Code providers: ${report.summary.availableCodeProviders}`);
|
|
2952
|
+
if (report.summary.blockingIssues.length > 0) {
|
|
2953
|
+
console.log(' Blockers:');
|
|
2954
|
+
for (const issue of report.summary.blockingIssues)
|
|
2955
|
+
console.log(` - ${issue}`);
|
|
2956
|
+
}
|
|
2957
|
+
if (report.warnings.length > 0) {
|
|
2958
|
+
console.log(` Warnings${report.warnings.length > 12 ? ` (showing first 12 of ${report.warnings.length})` : ''}:`);
|
|
2959
|
+
for (const warning of report.warnings.slice(0, 12))
|
|
2960
|
+
console.log(` - ${warning}`);
|
|
2961
|
+
}
|
|
2962
|
+
if (report.recommendations.length > 0) {
|
|
2963
|
+
console.log(' Next:');
|
|
2964
|
+
for (const command of report.recommendations.slice(0, 12))
|
|
2965
|
+
console.log(` ${command}`);
|
|
2966
|
+
}
|
|
2967
|
+
}
|
|
2890
2968
|
const configProfile = defineCommand({
|
|
2891
2969
|
meta: { name: 'profile', description: 'View or switch configuration profile' },
|
|
2892
2970
|
args: {
|
|
@@ -3635,9 +3713,125 @@ const upgradeRollback = defineCommand({
|
|
|
3635
3713
|
},
|
|
3636
3714
|
});
|
|
3637
3715
|
const upgrade = defineCommand({
|
|
3638
|
-
meta: { name: 'upgrade', description: 'SCALE 工作流、模板、skills、MCP、CLI
|
|
3716
|
+
meta: { name: 'upgrade', description: 'SCALE 工作流、模板、skills、MCP、CLI 工具的安全升级向导 / Safe update wizard for workflow assets' },
|
|
3717
|
+
args: {
|
|
3718
|
+
dir: { type: 'string', default: PROJECT_DIR, description: '项目目录 / Project directory' },
|
|
3719
|
+
'target-version': { type: 'string', description: '目标 SCALE Engine 版本,默认使用当前 CLI 版本 / Target SCALE Engine version' },
|
|
3720
|
+
apply: { type: 'boolean', default: false, description: '直接应用安全升级计划 / Apply safe upgrade plan' },
|
|
3721
|
+
yes: { type: 'boolean', default: false, description: '非交互确认 / Confirm without prompting' },
|
|
3722
|
+
html: { type: 'boolean', default: true, description: '写入 HTML 升级计划 / Write HTML plan' },
|
|
3723
|
+
interactive: { type: 'boolean', default: true, description: '启用升级向导交互 / Enable upgrade wizard prompts' },
|
|
3724
|
+
lang: { type: 'string', default: 'zh', description: '输出语言 zh/en / Output language' },
|
|
3725
|
+
json: { type: 'boolean', default: false, description: '输出 JSON / Print JSON output' },
|
|
3726
|
+
},
|
|
3639
3727
|
subCommands: { check: upgradeCheck, plan: upgradePlan, apply: upgradeApply, rollback: upgradeRollback },
|
|
3728
|
+
async run({ args }) {
|
|
3729
|
+
if (isUpgradeSubcommandInvocation(process.argv))
|
|
3730
|
+
return;
|
|
3731
|
+
const lang = normalizeLangArg(args.lang);
|
|
3732
|
+
const projectDir = resolve(String(args.dir ?? PROJECT_DIR));
|
|
3733
|
+
const scaleDir = resolveScaleDirForProject(projectDir);
|
|
3734
|
+
const targetScaleVersion = args['target-version'] ? String(args['target-version']) : undefined;
|
|
3735
|
+
const plan = createUpgradePlanReport({ projectDir, scaleDir, targetScaleVersion });
|
|
3736
|
+
const htmlPath = args.html ? writeUpgradePlanHtml(plan, undefined, lang) : undefined;
|
|
3737
|
+
const canApply = plan.applyMode === 'safe' && plan.blockers.length === 0;
|
|
3738
|
+
const interactive = isTruthyFlag(args.interactive) && !args.json && Boolean(process.stdin.isTTY) && !isTruthyFlag(args.yes);
|
|
3739
|
+
let apply = isTruthyFlag(args.apply) || isTruthyFlag(args.yes);
|
|
3740
|
+
let cancelled = false;
|
|
3741
|
+
if (interactive && canApply && !apply) {
|
|
3742
|
+
const answer = await askUpgradeWizardQuestion(lang === 'zh'
|
|
3743
|
+
? '发现可安全应用的升级计划。现在应用吗?1=仅生成计划 2=应用 3=取消,默认 1: '
|
|
3744
|
+
: 'Safe upgrade plan found. Apply now? 1=plan only 2=apply 3=cancel, default 1: ');
|
|
3745
|
+
const normalized = answer.trim().toLowerCase();
|
|
3746
|
+
apply = normalized === '2' || normalized === 'apply' || normalized === 'yes' || normalized === 'y';
|
|
3747
|
+
cancelled = normalized === '3' || normalized === 'cancel' || normalized === 'c';
|
|
3748
|
+
}
|
|
3749
|
+
const applyResult = apply && !cancelled
|
|
3750
|
+
? applyUpgradePlan({ projectDir, scaleDir, confirm: true })
|
|
3751
|
+
: undefined;
|
|
3752
|
+
const ok = !cancelled && (!applyResult || applyResult.ok);
|
|
3753
|
+
const report = { ok, cancelled, htmlPath, plan, applyResult };
|
|
3754
|
+
if (args.json) {
|
|
3755
|
+
console.log(JSON.stringify(report, null, 2));
|
|
3756
|
+
if (!ok)
|
|
3757
|
+
process.exitCode = 1;
|
|
3758
|
+
return;
|
|
3759
|
+
}
|
|
3760
|
+
renderUpgradeWizardReport(report, lang);
|
|
3761
|
+
if (!ok)
|
|
3762
|
+
process.exitCode = 1;
|
|
3763
|
+
},
|
|
3640
3764
|
});
|
|
3765
|
+
async function askUpgradeWizardQuestion(question) {
|
|
3766
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
3767
|
+
try {
|
|
3768
|
+
return await new Promise(resolve => rl.question(question, resolve));
|
|
3769
|
+
}
|
|
3770
|
+
finally {
|
|
3771
|
+
rl.close();
|
|
3772
|
+
}
|
|
3773
|
+
}
|
|
3774
|
+
function isUpgradeSubcommandInvocation(argv) {
|
|
3775
|
+
const upgradeIndex = argv.findIndex((value, index) => index > 1 && value === 'upgrade');
|
|
3776
|
+
if (upgradeIndex < 0)
|
|
3777
|
+
return false;
|
|
3778
|
+
const positional = argv.slice(upgradeIndex + 1).find(value => !value.startsWith('-') && !value.includes('='));
|
|
3779
|
+
return positional === 'check' || positional === 'plan' || positional === 'apply' || positional === 'rollback';
|
|
3780
|
+
}
|
|
3781
|
+
function renderUpgradeWizardReport(report, lang) {
|
|
3782
|
+
const plan = report.plan;
|
|
3783
|
+
if (lang === 'zh') {
|
|
3784
|
+
console.log('\nSCALE 升级向导');
|
|
3785
|
+
console.log(` 项目: ${plan.projectDir}`);
|
|
3786
|
+
console.log(` 状态: ${plan.status}`);
|
|
3787
|
+
console.log(` 应用模式: ${plan.applyMode}`);
|
|
3788
|
+
console.log(` 阻塞项: ${plan.blockers.length}`);
|
|
3789
|
+
console.log(` 步骤数: ${plan.steps.length}`);
|
|
3790
|
+
if (report.htmlPath)
|
|
3791
|
+
console.log(` HTML 计划: ${report.htmlPath}`);
|
|
3792
|
+
if (report.cancelled)
|
|
3793
|
+
console.log(' 结果: 已取消');
|
|
3794
|
+
else if (report.applyResult) {
|
|
3795
|
+
console.log(` 结果: ${report.applyResult.applied ? '已应用' : '未应用'}`);
|
|
3796
|
+
console.log(` 原因: ${formatUpgradeApplyReason(report.applyResult.reason, lang)}`);
|
|
3797
|
+
for (const path of report.applyResult.changedFiles)
|
|
3798
|
+
console.log(` 已变更: ${path}`);
|
|
3799
|
+
}
|
|
3800
|
+
else {
|
|
3801
|
+
console.log(' 结果: 已生成计划,未应用变更');
|
|
3802
|
+
}
|
|
3803
|
+
console.log(' 下一步:');
|
|
3804
|
+
for (const command of plan.check.recommendedCommands.slice(0, 5))
|
|
3805
|
+
console.log(` ${formatUpgradeCommand(command, lang)}`);
|
|
3806
|
+
if (plan.blockers.length > 0)
|
|
3807
|
+
console.log(' 先解决阻塞项后再执行 scale upgrade apply --confirm');
|
|
3808
|
+
return;
|
|
3809
|
+
}
|
|
3810
|
+
console.log('\nSCALE Upgrade Wizard');
|
|
3811
|
+
console.log(` Project: ${plan.projectDir}`);
|
|
3812
|
+
console.log(` Status: ${plan.status}`);
|
|
3813
|
+
console.log(` Apply mode: ${plan.applyMode}`);
|
|
3814
|
+
console.log(` Blockers: ${plan.blockers.length}`);
|
|
3815
|
+
console.log(` Steps: ${plan.steps.length}`);
|
|
3816
|
+
if (report.htmlPath)
|
|
3817
|
+
console.log(` HTML plan: ${report.htmlPath}`);
|
|
3818
|
+
if (report.cancelled)
|
|
3819
|
+
console.log(' Result: cancelled');
|
|
3820
|
+
else if (report.applyResult) {
|
|
3821
|
+
console.log(` Result: ${report.applyResult.applied ? 'applied' : 'not applied'}`);
|
|
3822
|
+
console.log(` Reason: ${report.applyResult.reason}`);
|
|
3823
|
+
for (const path of report.applyResult.changedFiles)
|
|
3824
|
+
console.log(` changed: ${path}`);
|
|
3825
|
+
}
|
|
3826
|
+
else {
|
|
3827
|
+
console.log(' Result: plan generated; no changes applied');
|
|
3828
|
+
}
|
|
3829
|
+
console.log(' Next:');
|
|
3830
|
+
for (const command of plan.check.recommendedCommands.slice(0, 5))
|
|
3831
|
+
console.log(` ${formatUpgradeCommand(command, lang)}`);
|
|
3832
|
+
if (plan.blockers.length > 0)
|
|
3833
|
+
console.log(' Resolve blockers before running scale upgrade apply --confirm');
|
|
3834
|
+
}
|
|
3641
3835
|
function formatUpgradeBlockerMessage(code, fallback, lang) {
|
|
3642
3836
|
if (lang !== 'zh')
|
|
3643
3837
|
return fallback;
|
|
@@ -4416,6 +4610,169 @@ function normalizeRuntimeSessionStatus(value) {
|
|
|
4416
4610
|
return normalized;
|
|
4417
4611
|
throw new Error(`Invalid runtime session status "${normalized}"; expected active, completed, failed, or abandoned.`);
|
|
4418
4612
|
}
|
|
4613
|
+
function parseNonNegativeNumberArg(value, name) {
|
|
4614
|
+
if (value === undefined || value === null || value === '')
|
|
4615
|
+
return undefined;
|
|
4616
|
+
const parsed = Number(value);
|
|
4617
|
+
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
4618
|
+
throw new Error(`${name} must be a non-negative number.`);
|
|
4619
|
+
}
|
|
4620
|
+
return parsed;
|
|
4621
|
+
}
|
|
4622
|
+
function parseJsonArg(value, name) {
|
|
4623
|
+
try {
|
|
4624
|
+
return JSON.parse(String(value ?? 'null'));
|
|
4625
|
+
}
|
|
4626
|
+
catch {
|
|
4627
|
+
throw new Error(`${name} must be valid JSON.`);
|
|
4628
|
+
}
|
|
4629
|
+
}
|
|
4630
|
+
function parseMetadataJson(value, name = '--metadata-json') {
|
|
4631
|
+
const parsed = parseJsonArg(value ?? '{}', name);
|
|
4632
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
4633
|
+
throw new Error(`${name} must be a JSON object.`);
|
|
4634
|
+
}
|
|
4635
|
+
return parsed;
|
|
4636
|
+
}
|
|
4637
|
+
function hasModelUsageArgs(args) {
|
|
4638
|
+
return [
|
|
4639
|
+
'provider',
|
|
4640
|
+
'model',
|
|
4641
|
+
'usage-json',
|
|
4642
|
+
'usage-file',
|
|
4643
|
+
'input-tokens',
|
|
4644
|
+
'output-tokens',
|
|
4645
|
+
'cache-eligible-tokens',
|
|
4646
|
+
'cache-creation-input-tokens',
|
|
4647
|
+
'cache-read-input-tokens',
|
|
4648
|
+
'cached-tokens',
|
|
4649
|
+
'estimated-cost-usd',
|
|
4650
|
+
].some(key => args[key] !== undefined && args[key] !== '');
|
|
4651
|
+
}
|
|
4652
|
+
function buildModelUsageRecordInput(args, defaults = {}) {
|
|
4653
|
+
const usagePayload = args['usage-file']
|
|
4654
|
+
? parseJsonArg(readFileSync(resolve(PROJECT_DIR, String(args['usage-file'])), 'utf-8'), '--usage-file')
|
|
4655
|
+
: args['usage-json']
|
|
4656
|
+
? parseJsonArg(args['usage-json'], '--usage-json')
|
|
4657
|
+
: undefined;
|
|
4658
|
+
const provider = String(args.provider ?? defaults.provider ?? '').trim();
|
|
4659
|
+
if (!provider)
|
|
4660
|
+
throw new Error('Model usage recording requires --provider.');
|
|
4661
|
+
return buildModelUsageInput({
|
|
4662
|
+
provider,
|
|
4663
|
+
model: args.model ? String(args.model) : undefined,
|
|
4664
|
+
taskId: args['task-id'] ? String(args['task-id']) : defaults.taskId,
|
|
4665
|
+
sessionId: args['session-id'] ? String(args['session-id']) : defaults.sessionId,
|
|
4666
|
+
inputTokens: parseNonNegativeNumberArg(args['input-tokens'], '--input-tokens'),
|
|
4667
|
+
outputTokens: parseNonNegativeNumberArg(args['output-tokens'], '--output-tokens'),
|
|
4668
|
+
cacheEligibleTokens: parseNonNegativeNumberArg(args['cache-eligible-tokens'], '--cache-eligible-tokens'),
|
|
4669
|
+
cacheCreationInputTokens: parseNonNegativeNumberArg(args['cache-creation-input-tokens'], '--cache-creation-input-tokens'),
|
|
4670
|
+
cacheReadInputTokens: parseNonNegativeNumberArg(args['cache-read-input-tokens'], '--cache-read-input-tokens'),
|
|
4671
|
+
cachedTokens: parseNonNegativeNumberArg(args['cached-tokens'], '--cached-tokens'),
|
|
4672
|
+
estimatedCostUsd: parseNonNegativeNumberArg(args['estimated-cost-usd'], '--estimated-cost-usd'),
|
|
4673
|
+
metadata: args['metadata-json'] !== undefined ? parseMetadataJson(args['metadata-json']) : undefined,
|
|
4674
|
+
timestamp: args.timestamp ? String(args.timestamp) : undefined,
|
|
4675
|
+
usagePayload,
|
|
4676
|
+
});
|
|
4677
|
+
}
|
|
4678
|
+
const tokenRecord = defineCommand({
|
|
4679
|
+
meta: { name: 'record', description: 'Record real model usage from provider usage payloads or explicit token counts' },
|
|
4680
|
+
args: {
|
|
4681
|
+
provider: { type: 'string', required: true, description: 'Model provider: anthropic, openai, codex, etc.' },
|
|
4682
|
+
model: { type: 'string', description: 'Optional model id' },
|
|
4683
|
+
'task-id': { type: 'string', description: 'Task id linked to this model usage' },
|
|
4684
|
+
'session-id': { type: 'string', description: 'Session id linked to this model usage' },
|
|
4685
|
+
'usage-json': { type: 'string', description: 'Raw provider response or usage JSON to normalize into the usage ledger' },
|
|
4686
|
+
'usage-file': { type: 'string', description: 'Path to a JSON file containing a raw provider response or usage payload' },
|
|
4687
|
+
'input-tokens': { type: 'string', description: 'Explicit input token count; overrides usage JSON when provided' },
|
|
4688
|
+
'output-tokens': { type: 'string', description: 'Explicit output token count; overrides usage JSON when provided' },
|
|
4689
|
+
'cache-eligible-tokens': { type: 'string', description: 'Explicit cache-eligible token count' },
|
|
4690
|
+
'cache-creation-input-tokens': { type: 'string', description: 'Explicit Anthropic cache creation token count' },
|
|
4691
|
+
'cache-read-input-tokens': { type: 'string', description: 'Explicit Anthropic cache read token count' },
|
|
4692
|
+
'cached-tokens': { type: 'string', description: 'Explicit OpenAI cached token count' },
|
|
4693
|
+
'estimated-cost-usd': { type: 'string', description: 'Optional estimated cost in USD' },
|
|
4694
|
+
timestamp: { type: 'string', description: 'Optional ISO timestamp' },
|
|
4695
|
+
'metadata-json': { type: 'string', default: '{}', description: 'Additional JSON metadata' },
|
|
4696
|
+
json: { type: 'boolean', default: false },
|
|
4697
|
+
},
|
|
4698
|
+
run({ args }) {
|
|
4699
|
+
const record = new ModelUsageLedger(SCALE_DIR).record(buildModelUsageRecordInput(args));
|
|
4700
|
+
if (args.json) {
|
|
4701
|
+
console.log(JSON.stringify(record, null, 2));
|
|
4702
|
+
return;
|
|
4703
|
+
}
|
|
4704
|
+
console.log(`Model usage recorded: ${record.id}`);
|
|
4705
|
+
console.log(` Provider: ${record.provider}`);
|
|
4706
|
+
console.log(` Model: ${record.model ?? 'unknown'}`);
|
|
4707
|
+
console.log(` Tokens: input ${record.inputTokens}, output ${record.outputTokens}, total ${record.totalTokens}`);
|
|
4708
|
+
if (record.cacheSavingsTokens > 0)
|
|
4709
|
+
console.log(` Cache savings: ${record.cacheSavingsTokens} tokens`);
|
|
4710
|
+
},
|
|
4711
|
+
});
|
|
4712
|
+
const tokenReport = defineCommand({
|
|
4713
|
+
meta: { name: 'report', description: 'Summarize recorded model usage by day, provider, model, and task' },
|
|
4714
|
+
args: {
|
|
4715
|
+
day: { type: 'string', description: 'Exact UTC day in YYYY-MM-DD format' },
|
|
4716
|
+
since: { type: 'string', description: 'ISO timestamp lower bound' },
|
|
4717
|
+
until: { type: 'string', description: 'ISO timestamp upper bound' },
|
|
4718
|
+
'since-days': { type: 'string', default: '7d', description: 'Relative time window when day/since/until are omitted; use all to disable' },
|
|
4719
|
+
provider: { type: 'string', description: 'Filter by provider' },
|
|
4720
|
+
model: { type: 'string', description: 'Filter by model id' },
|
|
4721
|
+
'task-id': { type: 'string', description: 'Filter by task id' },
|
|
4722
|
+
'session-id': { type: 'string', description: 'Filter by session id' },
|
|
4723
|
+
limit: { type: 'string', description: 'Maximum recent records to include in the report; defaults to 20' },
|
|
4724
|
+
json: { type: 'boolean', default: false },
|
|
4725
|
+
},
|
|
4726
|
+
run({ args }) {
|
|
4727
|
+
const limit = parsePositiveIntArg(args.limit, '--limit');
|
|
4728
|
+
const sinceDays = args.day || args.since || args.until ? undefined : parseSinceDays(args['since-days']) ?? 7;
|
|
4729
|
+
const since = args.since
|
|
4730
|
+
? String(args.since)
|
|
4731
|
+
: sinceDays
|
|
4732
|
+
? new Date(Date.now() - sinceDays * 24 * 60 * 60 * 1000).toISOString()
|
|
4733
|
+
: undefined;
|
|
4734
|
+
const report = new ModelUsageLedger(SCALE_DIR).report({
|
|
4735
|
+
day: args.day ? String(args.day) : undefined,
|
|
4736
|
+
since,
|
|
4737
|
+
until: args.until ? String(args.until) : undefined,
|
|
4738
|
+
provider: args.provider ? String(args.provider) : undefined,
|
|
4739
|
+
model: args.model ? String(args.model) : undefined,
|
|
4740
|
+
taskId: args['task-id'] ? String(args['task-id']) : undefined,
|
|
4741
|
+
sessionId: args['session-id'] ? String(args['session-id']) : undefined,
|
|
4742
|
+
limit,
|
|
4743
|
+
});
|
|
4744
|
+
if (args.json) {
|
|
4745
|
+
console.log(JSON.stringify(report, null, 2));
|
|
4746
|
+
return;
|
|
4747
|
+
}
|
|
4748
|
+
console.log('SCALE Token Report');
|
|
4749
|
+
if (report.filters.day)
|
|
4750
|
+
console.log(` Day: ${report.filters.day}`);
|
|
4751
|
+
else if (report.filters.since || report.filters.until)
|
|
4752
|
+
console.log(` Window: ${report.filters.since ?? '-inf'} -> ${report.filters.until ?? 'now'}`);
|
|
4753
|
+
console.log(` Records: ${report.summary.totalRecords}`);
|
|
4754
|
+
console.log(` Tokens: input ${report.summary.totalInputTokens}, output ${report.summary.totalOutputTokens}, total ${report.summary.totalTokens}`);
|
|
4755
|
+
console.log(` Cache: eligible ${report.summary.cacheEligibleTokens}, create ${report.summary.cacheCreationInputTokens}, read ${report.summary.cacheReadInputTokens}, cached ${report.summary.cachedTokens}, saved ${report.summary.cacheSavingsTokens}`);
|
|
4756
|
+
if (report.summary.estimatedCostUsd !== undefined)
|
|
4757
|
+
console.log(` Estimated cost: $${report.summary.estimatedCostUsd.toFixed(6)}`);
|
|
4758
|
+
for (const row of report.byProvider.slice(0, 5)) {
|
|
4759
|
+
console.log(` Provider ${row.key}: ${row.records} record(s), ${row.totalTokens} total tokens, ${row.cacheSavingsTokens} saved`);
|
|
4760
|
+
}
|
|
4761
|
+
for (const row of report.byModel.slice(0, 5)) {
|
|
4762
|
+
console.log(` Model ${row.key}: ${row.records} record(s), ${row.totalTokens} total tokens`);
|
|
4763
|
+
}
|
|
4764
|
+
for (const row of report.byTask.slice(0, 5)) {
|
|
4765
|
+
console.log(` Task ${row.key}: ${row.records} record(s), ${row.totalTokens} total tokens`);
|
|
4766
|
+
}
|
|
4767
|
+
for (const row of report.records.slice(0, 10)) {
|
|
4768
|
+
console.log(` Recent ${row.timestamp}: ${row.provider}/${row.model ?? 'unknown'} task=${row.taskId ?? '-'} total=${row.totalTokens}`);
|
|
4769
|
+
}
|
|
4770
|
+
},
|
|
4771
|
+
});
|
|
4772
|
+
const token = defineCommand({
|
|
4773
|
+
meta: { name: 'token', description: 'Record and audit real model token usage' },
|
|
4774
|
+
subCommands: { record: tokenRecord, report: tokenReport },
|
|
4775
|
+
});
|
|
4419
4776
|
const runtimeStart = defineCommand({
|
|
4420
4777
|
meta: { name: 'start', description: 'Start a runtime session ledger' },
|
|
4421
4778
|
args: {
|
|
@@ -4483,6 +4840,18 @@ const runtimeRecord = defineCommand({
|
|
|
4483
4840
|
'exit-code': { type: 'string', description: 'Exit code when applicable' },
|
|
4484
4841
|
summary: { type: 'string', required: true, description: 'Short output summary' },
|
|
4485
4842
|
artifacts: { type: 'string', description: 'Comma-separated artifact paths' },
|
|
4843
|
+
provider: { type: 'string', description: 'Optional model provider when attaching model usage: anthropic, openai, codex, etc.' },
|
|
4844
|
+
model: { type: 'string', description: 'Optional model id when attaching model usage' },
|
|
4845
|
+
'usage-json': { type: 'string', description: 'Raw provider response or usage JSON to normalize into the usage ledger' },
|
|
4846
|
+
'usage-file': { type: 'string', description: 'Path to a JSON file containing a raw provider response or usage payload' },
|
|
4847
|
+
'input-tokens': { type: 'string', description: 'Explicit input token count; overrides usage JSON when provided' },
|
|
4848
|
+
'output-tokens': { type: 'string', description: 'Explicit output token count; overrides usage JSON when provided' },
|
|
4849
|
+
'cache-eligible-tokens': { type: 'string', description: 'Explicit cache-eligible token count' },
|
|
4850
|
+
'cache-creation-input-tokens': { type: 'string', description: 'Explicit Anthropic cache creation token count' },
|
|
4851
|
+
'cache-read-input-tokens': { type: 'string', description: 'Explicit Anthropic cache read token count' },
|
|
4852
|
+
'cached-tokens': { type: 'string', description: 'Explicit OpenAI cached token count' },
|
|
4853
|
+
'estimated-cost-usd': { type: 'string', description: 'Optional estimated cost in USD' },
|
|
4854
|
+
timestamp: { type: 'string', description: 'Optional ISO timestamp for the usage record' },
|
|
4486
4855
|
'metadata-json': { type: 'string', default: '{}', description: 'Additional JSON metadata' },
|
|
4487
4856
|
json: { type: 'boolean', default: false },
|
|
4488
4857
|
},
|
|
@@ -4527,13 +4896,24 @@ const runtimeRecord = defineCommand({
|
|
|
4527
4896
|
},
|
|
4528
4897
|
});
|
|
4529
4898
|
}
|
|
4899
|
+
const usageRecord = hasModelUsageArgs(args)
|
|
4900
|
+
? new ModelUsageLedger(SCALE_DIR).record(buildModelUsageRecordInput(args, {
|
|
4901
|
+
taskId: record.taskId,
|
|
4902
|
+
sessionId: record.sessionId,
|
|
4903
|
+
}))
|
|
4904
|
+
: undefined;
|
|
4530
4905
|
if (args.json) {
|
|
4531
|
-
console.log(JSON.stringify(record, null, 2));
|
|
4906
|
+
console.log(JSON.stringify(usageRecord ? { evidence: record, usage: usageRecord } : record, null, 2));
|
|
4532
4907
|
return;
|
|
4533
4908
|
}
|
|
4534
4909
|
console.log(`Runtime evidence recorded: ${record.id}`);
|
|
4535
4910
|
console.log(` Status: ${record.status}`);
|
|
4536
4911
|
console.log(` Kind: ${record.kind}`);
|
|
4912
|
+
if (usageRecord) {
|
|
4913
|
+
console.log(` Model usage: ${usageRecord.provider}/${usageRecord.model ?? 'unknown'} ${usageRecord.totalTokens} total tokens`);
|
|
4914
|
+
if (usageRecord.cacheSavingsTokens > 0)
|
|
4915
|
+
console.log(` Cache savings: ${usageRecord.cacheSavingsTokens} tokens`);
|
|
4916
|
+
}
|
|
4537
4917
|
if (record.redactionApplied)
|
|
4538
4918
|
console.log(' Redaction: applied');
|
|
4539
4919
|
},
|
|
@@ -6153,6 +6533,7 @@ const main = defineCommand({
|
|
|
6153
6533
|
workflow,
|
|
6154
6534
|
evidence,
|
|
6155
6535
|
runtime,
|
|
6536
|
+
token,
|
|
6156
6537
|
memory,
|
|
6157
6538
|
diagnose,
|
|
6158
6539
|
hunt,
|
|
@@ -6169,5 +6550,19 @@ const main = defineCommand({
|
|
|
6169
6550
|
config,
|
|
6170
6551
|
},
|
|
6171
6552
|
});
|
|
6553
|
+
normalizeUpgradeRootOptionValues(process.argv);
|
|
6172
6554
|
runMain(main);
|
|
6555
|
+
function normalizeUpgradeRootOptionValues(argv) {
|
|
6556
|
+
const upgradeIndex = argv.findIndex((value, index) => index > 1 && value === 'upgrade');
|
|
6557
|
+
if (upgradeIndex < 0)
|
|
6558
|
+
return;
|
|
6559
|
+
for (let index = upgradeIndex + 1; index < argv.length - 1; index += 1) {
|
|
6560
|
+
if (!['--dir', '--target-version', '--lang'].includes(argv[index]))
|
|
6561
|
+
continue;
|
|
6562
|
+
const value = argv[index + 1];
|
|
6563
|
+
if (!value || value.startsWith('--'))
|
|
6564
|
+
continue;
|
|
6565
|
+
argv.splice(index, 2, `${argv[index]}=${value}`);
|
|
6566
|
+
}
|
|
6567
|
+
}
|
|
6173
6568
|
//# sourceMappingURL=cli.js.map
|