scene-capability-engine 3.6.2 → 3.6.4
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 +54 -0
- package/README.md +15 -2
- package/README.zh.md +15 -2
- package/bin/scene-capability-engine.js +33 -1
- package/docs/command-reference.md +87 -0
- package/lib/collab/agent-registry.js +38 -1
- package/lib/commands/auth.js +269 -0
- package/lib/commands/session.js +60 -2
- package/lib/commands/state.js +210 -0
- package/lib/commands/studio.js +57 -7
- package/lib/commands/task.js +25 -2
- package/lib/runtime/project-timeline.js +202 -17
- package/lib/runtime/session-store.js +167 -14
- package/lib/security/write-authorization.js +632 -0
- package/lib/state/sce-state-store.js +1029 -1
- package/lib/state/state-migration-manager.js +659 -0
- package/lib/steering/compliance-error-reporter.js +6 -0
- package/lib/steering/steering-compliance-checker.js +43 -8
- package/package.json +2 -1
package/lib/commands/session.js
CHANGED
|
@@ -102,7 +102,32 @@ function registerSessionCommands(program) {
|
|
|
102
102
|
try {
|
|
103
103
|
const store = new SessionStore(process.cwd());
|
|
104
104
|
const current = await store.getSession(sessionRef || 'latest');
|
|
105
|
-
|
|
105
|
+
const sceneIndex = await store.getSceneIndexDiagnostics();
|
|
106
|
+
_printSessionResult('session_show', current, options.json, {
|
|
107
|
+
session_source: 'file',
|
|
108
|
+
scene_index: sceneIndex
|
|
109
|
+
});
|
|
110
|
+
} catch (error) {
|
|
111
|
+
_exitWithError(error, options.json);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
session
|
|
116
|
+
.command('list')
|
|
117
|
+
.description('List recent sessions')
|
|
118
|
+
.option('--limit <n>', 'Maximum sessions to return', '20')
|
|
119
|
+
.option('--json', 'Output as JSON')
|
|
120
|
+
.action(async (options) => {
|
|
121
|
+
try {
|
|
122
|
+
const store = new SessionStore(process.cwd());
|
|
123
|
+
const limitRaw = Number.parseInt(`${options.limit || '20'}`, 10);
|
|
124
|
+
const limit = Number.isFinite(limitRaw) && limitRaw > 0 ? Math.min(limitRaw, 5000) : 20;
|
|
125
|
+
const sessions = await store.listSessions();
|
|
126
|
+
const sceneIndex = await store.getSceneIndexDiagnostics();
|
|
127
|
+
_printSessionListResult('session_list', sessions.slice(0, limit), options.json, {
|
|
128
|
+
session_source: 'file',
|
|
129
|
+
scene_index: sceneIndex
|
|
130
|
+
});
|
|
106
131
|
} catch (error) {
|
|
107
132
|
_exitWithError(error, options.json);
|
|
108
133
|
}
|
|
@@ -120,12 +145,13 @@ function _parsePayload(raw) {
|
|
|
120
145
|
}
|
|
121
146
|
}
|
|
122
147
|
|
|
123
|
-
function _printSessionResult(action, session, asJson = false) {
|
|
148
|
+
function _printSessionResult(action, session, asJson = false, metadata = {}) {
|
|
124
149
|
if (asJson) {
|
|
125
150
|
console.log(JSON.stringify({
|
|
126
151
|
success: true,
|
|
127
152
|
action,
|
|
128
153
|
session,
|
|
154
|
+
...metadata
|
|
129
155
|
}, null, 2));
|
|
130
156
|
return;
|
|
131
157
|
}
|
|
@@ -147,6 +173,38 @@ function _printSessionResult(action, session, asJson = false) {
|
|
|
147
173
|
if (session.objective) {
|
|
148
174
|
console.log(chalk.gray(`Objective: ${session.objective}`));
|
|
149
175
|
}
|
|
176
|
+
if (metadata.session_source) {
|
|
177
|
+
console.log(chalk.gray(`Session source: ${metadata.session_source}`));
|
|
178
|
+
}
|
|
179
|
+
if (metadata.scene_index && metadata.scene_index.status) {
|
|
180
|
+
console.log(chalk.gray(`Scene index consistency: ${metadata.scene_index.status}`));
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function _printSessionListResult(action, sessions, asJson = false, metadata = {}) {
|
|
185
|
+
if (asJson) {
|
|
186
|
+
console.log(JSON.stringify({
|
|
187
|
+
success: true,
|
|
188
|
+
action,
|
|
189
|
+
total: Array.isArray(sessions) ? sessions.length : 0,
|
|
190
|
+
sessions: Array.isArray(sessions) ? sessions : [],
|
|
191
|
+
...metadata
|
|
192
|
+
}, null, 2));
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
console.log(chalk.green('✓ Sessions listed'));
|
|
197
|
+
const list = Array.isArray(sessions) ? sessions : [];
|
|
198
|
+
console.log(chalk.gray(`Total: ${list.length}`));
|
|
199
|
+
if (metadata.session_source) {
|
|
200
|
+
console.log(chalk.gray(`Session source: ${metadata.session_source}`));
|
|
201
|
+
}
|
|
202
|
+
if (metadata.scene_index && metadata.scene_index.status) {
|
|
203
|
+
console.log(chalk.gray(`Scene index consistency: ${metadata.scene_index.status}`));
|
|
204
|
+
}
|
|
205
|
+
for (const session of list) {
|
|
206
|
+
console.log(`- ${session.session_id} | ${session.status} | ${session.updated_at || session.started_at || 'n/a'}`);
|
|
207
|
+
}
|
|
150
208
|
}
|
|
151
209
|
|
|
152
210
|
function _exitWithError(error, asJson = false) {
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const chalk = require('chalk');
|
|
3
|
+
const fs = require('fs-extra');
|
|
4
|
+
const {
|
|
5
|
+
COMPONENT_DEFINITIONS,
|
|
6
|
+
buildStateMigrationPlan,
|
|
7
|
+
runStateMigration,
|
|
8
|
+
runStateDoctor,
|
|
9
|
+
runStateExport
|
|
10
|
+
} = require('../state/state-migration-manager');
|
|
11
|
+
|
|
12
|
+
function normalizeString(value) {
|
|
13
|
+
if (typeof value !== 'string') {
|
|
14
|
+
return '';
|
|
15
|
+
}
|
|
16
|
+
return value.trim();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function collectOptionValue(value, previous = []) {
|
|
20
|
+
const normalized = normalizeString(value);
|
|
21
|
+
if (!normalized) {
|
|
22
|
+
return previous;
|
|
23
|
+
}
|
|
24
|
+
return [...previous, normalized];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function normalizeComponentInput(value = []) {
|
|
28
|
+
if (!Array.isArray(value)) {
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
const items = [];
|
|
32
|
+
for (const entry of value) {
|
|
33
|
+
const normalized = normalizeString(entry);
|
|
34
|
+
if (!normalized) {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
for (const token of normalized.split(/[,\s]+/g)) {
|
|
38
|
+
const cleaned = normalizeString(token);
|
|
39
|
+
if (cleaned) {
|
|
40
|
+
items.push(cleaned);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return Array.from(new Set(items));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function printPayload(payload, options = {}, title = 'State') {
|
|
48
|
+
if (options.json) {
|
|
49
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
console.log(chalk.blue(title));
|
|
54
|
+
if (payload.mode) {
|
|
55
|
+
console.log(` Mode: ${payload.mode}`);
|
|
56
|
+
}
|
|
57
|
+
if (payload.store_path) {
|
|
58
|
+
console.log(` Store: ${payload.store_path}`);
|
|
59
|
+
}
|
|
60
|
+
if (payload.sqlite) {
|
|
61
|
+
console.log(` SQLite: configured=${payload.sqlite.configured ? 'yes' : 'no'} available=${payload.sqlite.available ? 'yes' : 'no'}`);
|
|
62
|
+
}
|
|
63
|
+
if (payload.summary && typeof payload.summary === 'object') {
|
|
64
|
+
for (const [key, value] of Object.entries(payload.summary)) {
|
|
65
|
+
console.log(` ${key}: ${value}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (Array.isArray(payload.components)) {
|
|
69
|
+
for (const item of payload.components) {
|
|
70
|
+
console.log(` - ${item.id} | source=${item.source_record_count} | status=${item.status}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (Array.isArray(payload.operations)) {
|
|
74
|
+
for (const item of payload.operations) {
|
|
75
|
+
console.log(` - ${item.component_id} | ${item.status} | source=${item.source_record_count}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (payload.out_file) {
|
|
79
|
+
console.log(` Export: ${payload.out_file}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function runStatePlanCommand(options = {}, dependencies = {}) {
|
|
84
|
+
const projectPath = dependencies.projectPath || process.cwd();
|
|
85
|
+
const fileSystem = dependencies.fileSystem || fs;
|
|
86
|
+
const env = dependencies.env || process.env;
|
|
87
|
+
const components = normalizeComponentInput(options.component);
|
|
88
|
+
|
|
89
|
+
const payload = await buildStateMigrationPlan({
|
|
90
|
+
componentIds: components
|
|
91
|
+
}, {
|
|
92
|
+
projectPath,
|
|
93
|
+
fileSystem,
|
|
94
|
+
env
|
|
95
|
+
});
|
|
96
|
+
printPayload(payload, options, 'State Plan');
|
|
97
|
+
return payload;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async function runStateDoctorCommand(options = {}, dependencies = {}) {
|
|
101
|
+
const projectPath = dependencies.projectPath || process.cwd();
|
|
102
|
+
const fileSystem = dependencies.fileSystem || fs;
|
|
103
|
+
const env = dependencies.env || process.env;
|
|
104
|
+
|
|
105
|
+
const payload = await runStateDoctor({}, {
|
|
106
|
+
projectPath,
|
|
107
|
+
fileSystem,
|
|
108
|
+
env
|
|
109
|
+
});
|
|
110
|
+
printPayload(payload, options, 'State Doctor');
|
|
111
|
+
return payload;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async function runStateMigrateCommand(options = {}, dependencies = {}) {
|
|
115
|
+
const projectPath = dependencies.projectPath || process.cwd();
|
|
116
|
+
const fileSystem = dependencies.fileSystem || fs;
|
|
117
|
+
const env = dependencies.env || process.env;
|
|
118
|
+
const components = normalizeComponentInput(options.component);
|
|
119
|
+
const componentIds = options.all === true ? [] : components;
|
|
120
|
+
|
|
121
|
+
const payload = await runStateMigration({
|
|
122
|
+
apply: options.apply === true,
|
|
123
|
+
all: options.all === true,
|
|
124
|
+
componentIds
|
|
125
|
+
}, {
|
|
126
|
+
projectPath,
|
|
127
|
+
fileSystem,
|
|
128
|
+
env
|
|
129
|
+
});
|
|
130
|
+
printPayload(payload, options, 'State Migrate');
|
|
131
|
+
return payload;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async function runStateExportCommand(options = {}, dependencies = {}) {
|
|
135
|
+
const projectPath = dependencies.projectPath || process.cwd();
|
|
136
|
+
const fileSystem = dependencies.fileSystem || fs;
|
|
137
|
+
const env = dependencies.env || process.env;
|
|
138
|
+
|
|
139
|
+
const payload = await runStateExport({
|
|
140
|
+
out: normalizeString(options.out)
|
|
141
|
+
}, {
|
|
142
|
+
projectPath,
|
|
143
|
+
fileSystem,
|
|
144
|
+
env
|
|
145
|
+
});
|
|
146
|
+
printPayload(payload, options, 'State Export');
|
|
147
|
+
return payload;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async function safeRun(handler, options = {}, dependencies = {}, title = 'state command') {
|
|
151
|
+
try {
|
|
152
|
+
await handler(options, dependencies);
|
|
153
|
+
} catch (error) {
|
|
154
|
+
if (options && options.json) {
|
|
155
|
+
console.log(JSON.stringify({
|
|
156
|
+
success: false,
|
|
157
|
+
mode: title.replace(/\s+/g, '-'),
|
|
158
|
+
error: error.message
|
|
159
|
+
}, null, 2));
|
|
160
|
+
} else {
|
|
161
|
+
console.error(chalk.red(`${title} failed:`), error.message);
|
|
162
|
+
}
|
|
163
|
+
process.exitCode = 1;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function registerStateCommands(program) {
|
|
168
|
+
const state = program
|
|
169
|
+
.command('state')
|
|
170
|
+
.description('Manage gradual migration from file registries to sqlite indexes');
|
|
171
|
+
|
|
172
|
+
const knownIds = COMPONENT_DEFINITIONS.map((item) => item.id).join(', ');
|
|
173
|
+
|
|
174
|
+
state
|
|
175
|
+
.command('plan')
|
|
176
|
+
.description('Inspect migratable file-based registries and produce migration plan')
|
|
177
|
+
.option('--component <id>', `Component id (repeatable): ${knownIds}`, collectOptionValue, [])
|
|
178
|
+
.option('--json', 'Print machine-readable JSON output')
|
|
179
|
+
.action(async (options) => safeRun(runStatePlanCommand, options, {}, 'state plan'));
|
|
180
|
+
|
|
181
|
+
state
|
|
182
|
+
.command('doctor')
|
|
183
|
+
.description('Check sqlite readiness and file/sqlite index consistency')
|
|
184
|
+
.option('--json', 'Print machine-readable JSON output')
|
|
185
|
+
.action(async (options) => safeRun(runStateDoctorCommand, options, {}, 'state doctor'));
|
|
186
|
+
|
|
187
|
+
state
|
|
188
|
+
.command('migrate')
|
|
189
|
+
.description('Migrate selected components to sqlite indexes (dry-run by default)')
|
|
190
|
+
.option('--component <id>', `Component id (repeatable): ${knownIds}`, collectOptionValue, [])
|
|
191
|
+
.option('--all', 'Migrate all known components')
|
|
192
|
+
.option('--apply', 'Apply migration writes (default is dry-run)')
|
|
193
|
+
.option('--json', 'Print machine-readable JSON output')
|
|
194
|
+
.action(async (options) => safeRun(runStateMigrateCommand, options, {}, 'state migrate'));
|
|
195
|
+
|
|
196
|
+
state
|
|
197
|
+
.command('export')
|
|
198
|
+
.description('Export sqlite state migration tables as JSON snapshot')
|
|
199
|
+
.option('--out <path>', 'Output file path', '.sce/reports/state-migration/state-export.latest.json')
|
|
200
|
+
.option('--json', 'Print machine-readable JSON output')
|
|
201
|
+
.action(async (options) => safeRun(runStateExportCommand, options, {}, 'state export'));
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
module.exports = {
|
|
205
|
+
runStatePlanCommand,
|
|
206
|
+
runStateDoctorCommand,
|
|
207
|
+
runStateMigrateCommand,
|
|
208
|
+
runStateExportCommand,
|
|
209
|
+
registerStateCommands
|
|
210
|
+
};
|
package/lib/commands/studio.js
CHANGED
|
@@ -13,6 +13,7 @@ const { captureTimelineCheckpoint } = require('../runtime/project-timeline');
|
|
|
13
13
|
const { runProblemEvaluation } = require('../problem/problem-evaluator');
|
|
14
14
|
const { TaskRefRegistry } = require('../task/task-ref-registry');
|
|
15
15
|
const { getSceStateStore } = require('../state/sce-state-store');
|
|
16
|
+
const { ensureWriteAuthorization } = require('../security/write-authorization');
|
|
16
17
|
const {
|
|
17
18
|
loadStudioIntakePolicy,
|
|
18
19
|
runStudioAutoIntake,
|
|
@@ -2661,6 +2662,12 @@ async function runStudioApplyCommand(options = {}, dependencies = {}) {
|
|
|
2661
2662
|
const job = await loadJob(paths, jobId, fileSystem);
|
|
2662
2663
|
ensureNotRolledBack(job, 'apply');
|
|
2663
2664
|
ensureStagePrerequisite(job, 'apply', 'generate');
|
|
2665
|
+
const leaseAuthResult = await ensureWriteAuthorization('studio:apply', options, {
|
|
2666
|
+
projectPath,
|
|
2667
|
+
fileSystem,
|
|
2668
|
+
env: dependencies.env,
|
|
2669
|
+
authSecret: dependencies.authSecret
|
|
2670
|
+
});
|
|
2664
2671
|
const authResult = await ensureStudioAuthorization('apply', options, {
|
|
2665
2672
|
projectPath,
|
|
2666
2673
|
fileSystem,
|
|
@@ -2696,14 +2703,22 @@ async function runStudioApplyCommand(options = {}, dependencies = {}) {
|
|
|
2696
2703
|
|
|
2697
2704
|
ensureStageCompleted(job, 'apply', {
|
|
2698
2705
|
patch_bundle_id: patchBundleId,
|
|
2699
|
-
auth_required: authResult.required,
|
|
2706
|
+
auth_required: authResult.required || leaseAuthResult.required,
|
|
2707
|
+
auth_password_required: authResult.required,
|
|
2708
|
+
auth_lease_required: leaseAuthResult.required,
|
|
2709
|
+
auth_lease_id: leaseAuthResult.lease_id || null,
|
|
2710
|
+
auth_lease_expires_at: leaseAuthResult.lease_expires_at || null,
|
|
2700
2711
|
problem_evaluation: summarizeProblemEvaluation(applyProblemEvaluation)
|
|
2701
2712
|
});
|
|
2702
2713
|
|
|
2703
2714
|
await saveJob(paths, job, fileSystem);
|
|
2704
2715
|
const applyEvent = await appendStudioEvent(paths, job, 'stage.apply.completed', {
|
|
2705
2716
|
patch_bundle_id: patchBundleId,
|
|
2706
|
-
auth_required: authResult.required,
|
|
2717
|
+
auth_required: authResult.required || leaseAuthResult.required,
|
|
2718
|
+
auth_password_required: authResult.required,
|
|
2719
|
+
auth_lease_required: leaseAuthResult.required,
|
|
2720
|
+
auth_lease_id: leaseAuthResult.lease_id || null,
|
|
2721
|
+
auth_lease_expires_at: leaseAuthResult.lease_expires_at || null,
|
|
2707
2722
|
problem_evaluation: summarizeProblemEvaluation(applyProblemEvaluation)
|
|
2708
2723
|
}, fileSystem);
|
|
2709
2724
|
await writeLatestJob(paths, jobId, fileSystem);
|
|
@@ -2890,6 +2905,12 @@ async function runStudioReleaseCommand(options = {}, dependencies = {}) {
|
|
|
2890
2905
|
const job = await loadJob(paths, jobId, fileSystem);
|
|
2891
2906
|
ensureNotRolledBack(job, 'release');
|
|
2892
2907
|
ensureStagePrerequisite(job, 'release', 'verify');
|
|
2908
|
+
const leaseAuthResult = await ensureWriteAuthorization('studio:release', options, {
|
|
2909
|
+
projectPath,
|
|
2910
|
+
fileSystem,
|
|
2911
|
+
env: dependencies.env,
|
|
2912
|
+
authSecret: dependencies.authSecret
|
|
2913
|
+
});
|
|
2893
2914
|
const authResult = await ensureStudioAuthorization('release', options, {
|
|
2894
2915
|
projectPath,
|
|
2895
2916
|
fileSystem,
|
|
@@ -3006,7 +3027,11 @@ async function runStudioReleaseCommand(options = {}, dependencies = {}) {
|
|
|
3006
3027
|
passed: false,
|
|
3007
3028
|
report: releaseReportPath,
|
|
3008
3029
|
gate_steps: gateResult.steps,
|
|
3009
|
-
auth_required: authResult.required,
|
|
3030
|
+
auth_required: authResult.required || leaseAuthResult.required,
|
|
3031
|
+
auth_password_required: authResult.required,
|
|
3032
|
+
auth_lease_required: leaseAuthResult.required,
|
|
3033
|
+
auth_lease_id: leaseAuthResult.lease_id || null,
|
|
3034
|
+
auth_lease_expires_at: leaseAuthResult.lease_expires_at || null,
|
|
3010
3035
|
problem_evaluation: summarizeProblemEvaluation(releaseProblemEvaluation),
|
|
3011
3036
|
domain_chain: domainChainMetadata,
|
|
3012
3037
|
auto_errorbook_records: autoErrorbookRecords
|
|
@@ -3017,7 +3042,11 @@ async function runStudioReleaseCommand(options = {}, dependencies = {}) {
|
|
|
3017
3042
|
channel,
|
|
3018
3043
|
release_ref: releaseRef,
|
|
3019
3044
|
report: releaseReportPath,
|
|
3020
|
-
auth_required: authResult.required,
|
|
3045
|
+
auth_required: authResult.required || leaseAuthResult.required,
|
|
3046
|
+
auth_password_required: authResult.required,
|
|
3047
|
+
auth_lease_required: leaseAuthResult.required,
|
|
3048
|
+
auth_lease_id: leaseAuthResult.lease_id || null,
|
|
3049
|
+
auth_lease_expires_at: leaseAuthResult.lease_expires_at || null,
|
|
3021
3050
|
problem_evaluation: summarizeProblemEvaluation(releaseProblemEvaluation),
|
|
3022
3051
|
domain_chain: domainChainMetadata,
|
|
3023
3052
|
auto_errorbook_records: autoErrorbookRecords
|
|
@@ -3032,7 +3061,11 @@ async function runStudioReleaseCommand(options = {}, dependencies = {}) {
|
|
|
3032
3061
|
release_ref: releaseRef,
|
|
3033
3062
|
report: releaseReportPath,
|
|
3034
3063
|
gate_steps: gateResult.steps,
|
|
3035
|
-
auth_required: authResult.required,
|
|
3064
|
+
auth_required: authResult.required || leaseAuthResult.required,
|
|
3065
|
+
auth_password_required: authResult.required,
|
|
3066
|
+
auth_lease_required: leaseAuthResult.required,
|
|
3067
|
+
auth_lease_id: leaseAuthResult.lease_id || null,
|
|
3068
|
+
auth_lease_expires_at: leaseAuthResult.lease_expires_at || null,
|
|
3036
3069
|
problem_evaluation: summarizeProblemEvaluation(releaseProblemEvaluation),
|
|
3037
3070
|
domain_chain: domainChainMetadata,
|
|
3038
3071
|
auto_errorbook_records: autoErrorbookRecords
|
|
@@ -3064,7 +3097,11 @@ async function runStudioReleaseCommand(options = {}, dependencies = {}) {
|
|
|
3064
3097
|
channel,
|
|
3065
3098
|
release_ref: releaseRef,
|
|
3066
3099
|
report: releaseReportPath,
|
|
3067
|
-
auth_required: authResult.required,
|
|
3100
|
+
auth_required: authResult.required || leaseAuthResult.required,
|
|
3101
|
+
auth_password_required: authResult.required,
|
|
3102
|
+
auth_lease_required: leaseAuthResult.required,
|
|
3103
|
+
auth_lease_id: leaseAuthResult.lease_id || null,
|
|
3104
|
+
auth_lease_expires_at: leaseAuthResult.lease_expires_at || null,
|
|
3068
3105
|
problem_evaluation: summarizeProblemEvaluation(releaseProblemEvaluation),
|
|
3069
3106
|
domain_chain: domainChainMetadata,
|
|
3070
3107
|
auto_errorbook_records: autoErrorbookRecords
|
|
@@ -3119,6 +3156,12 @@ async function runStudioRollbackCommand(options = {}, dependencies = {}) {
|
|
|
3119
3156
|
|
|
3120
3157
|
const reason = normalizeString(options.reason) || 'manual-rollback';
|
|
3121
3158
|
const job = await loadJob(paths, jobId, fileSystem);
|
|
3159
|
+
const leaseAuthResult = await ensureWriteAuthorization('studio:rollback', options, {
|
|
3160
|
+
projectPath,
|
|
3161
|
+
fileSystem,
|
|
3162
|
+
env: dependencies.env,
|
|
3163
|
+
authSecret: dependencies.authSecret
|
|
3164
|
+
});
|
|
3122
3165
|
const authResult = await ensureStudioAuthorization('rollback', options, {
|
|
3123
3166
|
projectPath,
|
|
3124
3167
|
fileSystem,
|
|
@@ -3134,7 +3177,11 @@ async function runStudioRollbackCommand(options = {}, dependencies = {}) {
|
|
|
3134
3177
|
job.rollback = {
|
|
3135
3178
|
reason,
|
|
3136
3179
|
rolled_back_at: job.updated_at,
|
|
3137
|
-
auth_required: authResult.required
|
|
3180
|
+
auth_required: authResult.required || leaseAuthResult.required,
|
|
3181
|
+
auth_password_required: authResult.required,
|
|
3182
|
+
auth_lease_required: leaseAuthResult.required,
|
|
3183
|
+
auth_lease_id: leaseAuthResult.lease_id || null,
|
|
3184
|
+
auth_lease_expires_at: leaseAuthResult.lease_expires_at || null
|
|
3138
3185
|
};
|
|
3139
3186
|
|
|
3140
3187
|
const sceneSessionId = normalizeString(job && job.session && job.session.scene_session_id);
|
|
@@ -3508,6 +3555,7 @@ function registerStudioCommands(program) {
|
|
|
3508
3555
|
.command('apply')
|
|
3509
3556
|
.description('Apply generated patch bundle metadata to studio job')
|
|
3510
3557
|
.option('--patch-bundle <id>', 'Patch bundle identifier (defaults to generated artifact)')
|
|
3558
|
+
.option('--auth-lease <lease-id>', 'Write authorization lease id (sce auth grant)')
|
|
3511
3559
|
.option('--auth-password <password>', 'Authorization password for protected apply action')
|
|
3512
3560
|
.option('--require-auth', 'Require authorization even when policy is advisory')
|
|
3513
3561
|
.option('--job <job-id>', 'Studio job id (defaults to latest)')
|
|
@@ -3527,6 +3575,7 @@ function registerStudioCommands(program) {
|
|
|
3527
3575
|
.description('Record release stage for studio job')
|
|
3528
3576
|
.option('--channel <channel>', 'Release channel (dev|prod)', 'dev')
|
|
3529
3577
|
.option('--profile <profile>', 'Release gate profile', 'standard')
|
|
3578
|
+
.option('--auth-lease <lease-id>', 'Write authorization lease id (sce auth grant)')
|
|
3530
3579
|
.option('--auth-password <password>', 'Authorization password for protected release action')
|
|
3531
3580
|
.option('--require-auth', 'Require authorization even when policy is advisory')
|
|
3532
3581
|
.option('--release-ref <ref>', 'Explicit release reference/tag')
|
|
@@ -3555,6 +3604,7 @@ function registerStudioCommands(program) {
|
|
|
3555
3604
|
.description('Rollback a studio job after apply/release')
|
|
3556
3605
|
.option('--job <job-id>', 'Studio job id (defaults to latest)')
|
|
3557
3606
|
.option('--reason <reason>', 'Rollback reason')
|
|
3607
|
+
.option('--auth-lease <lease-id>', 'Write authorization lease id (sce auth grant)')
|
|
3558
3608
|
.option('--auth-password <password>', 'Authorization password for protected rollback action')
|
|
3559
3609
|
.option('--require-auth', 'Require authorization even when policy is advisory')
|
|
3560
3610
|
.option('--json', 'Print machine-readable JSON output')
|
package/lib/commands/task.js
CHANGED
|
@@ -11,6 +11,7 @@ const chalk = require('chalk');
|
|
|
11
11
|
const TaskClaimer = require('../task/task-claimer');
|
|
12
12
|
const WorkspaceManager = require('../workspace/workspace-manager');
|
|
13
13
|
const { TaskRefRegistry } = require('../task/task-ref-registry');
|
|
14
|
+
const { ensureWriteAuthorization } = require('../security/write-authorization');
|
|
14
15
|
|
|
15
16
|
function normalizeString(value) {
|
|
16
17
|
if (typeof value !== 'string') {
|
|
@@ -436,6 +437,17 @@ async function runTaskRerunCommand(options = {}, dependencies = {}) {
|
|
|
436
437
|
if (!lookup) {
|
|
437
438
|
throw new Error(`Task ref not found: ${taskRef}`);
|
|
438
439
|
}
|
|
440
|
+
const writeAuthResult = dryRun
|
|
441
|
+
? {
|
|
442
|
+
required: false,
|
|
443
|
+
passed: true
|
|
444
|
+
}
|
|
445
|
+
: await ensureWriteAuthorization('task:rerun', options, {
|
|
446
|
+
projectPath,
|
|
447
|
+
fileSystem,
|
|
448
|
+
env: dependencies.env,
|
|
449
|
+
authSecret: dependencies.authSecret
|
|
450
|
+
});
|
|
439
451
|
|
|
440
452
|
if (isStudioTaskRef(lookup)) {
|
|
441
453
|
const stage = resolveStudioStageFromTaskKey(lookup.task_key);
|
|
@@ -453,7 +465,12 @@ async function runTaskRerunCommand(options = {}, dependencies = {}) {
|
|
|
453
465
|
stage,
|
|
454
466
|
job_id: normalizeString(job?.job_id) || null,
|
|
455
467
|
dry_run: dryRun,
|
|
456
|
-
command: stringifySceArgs(rerunPlan.args)
|
|
468
|
+
command: stringifySceArgs(rerunPlan.args),
|
|
469
|
+
authorization: {
|
|
470
|
+
required: writeAuthResult.required === true,
|
|
471
|
+
lease_id: writeAuthResult.lease_id || null,
|
|
472
|
+
lease_expires_at: writeAuthResult.lease_expires_at || null
|
|
473
|
+
}
|
|
457
474
|
};
|
|
458
475
|
|
|
459
476
|
if (!dryRun) {
|
|
@@ -486,7 +503,12 @@ async function runTaskRerunCommand(options = {}, dependencies = {}) {
|
|
|
486
503
|
task_ref: lookup.task_ref,
|
|
487
504
|
rerun_type: 'spec-task',
|
|
488
505
|
dry_run: dryRun,
|
|
489
|
-
command: `sce task claim ${lookup.spec_id} ${lookup.task_key}
|
|
506
|
+
command: `sce task claim ${lookup.spec_id} ${lookup.task_key}`,
|
|
507
|
+
authorization: {
|
|
508
|
+
required: writeAuthResult.required === true,
|
|
509
|
+
lease_id: writeAuthResult.lease_id || null,
|
|
510
|
+
lease_expires_at: writeAuthResult.lease_expires_at || null
|
|
511
|
+
}
|
|
490
512
|
};
|
|
491
513
|
|
|
492
514
|
if (!dryRun) {
|
|
@@ -734,6 +756,7 @@ function registerTaskCommands(program) {
|
|
|
734
756
|
.description('Rerun task by hierarchical reference')
|
|
735
757
|
.requiredOption('--ref <task-ref>', 'Task reference (SS.PP.TT)')
|
|
736
758
|
.option('--dry-run', 'Preview rerun command without executing')
|
|
759
|
+
.option('--auth-lease <lease-id>', 'Write authorization lease id (sce auth grant)')
|
|
737
760
|
.option('--from-chat <session>', 'Override session for studio plan rerun')
|
|
738
761
|
.option('--job <job-id>', 'Override studio job id')
|
|
739
762
|
.option('--profile <profile>', 'Override profile for studio verify/release rerun')
|