openplanr 1.14.4 → 1.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/commands/operate.d.ts.map +1 -1
- package/dist/cli/commands/operate.js +54 -87
- package/dist/cli/commands/operate.js.map +1 -1
- package/dist/services/operate/config.d.ts +4 -1
- package/dist/services/operate/config.d.ts.map +1 -1
- package/dist/services/operate/config.js +56 -2
- package/dist/services/operate/config.js.map +1 -1
- package/dist/services/operate/doctor.d.ts.map +1 -1
- package/dist/services/operate/doctor.js +15 -0
- package/dist/services/operate/doctor.js.map +1 -1
- package/dist/services/operate/evidence-classifications.d.ts +59 -0
- package/dist/services/operate/evidence-classifications.d.ts.map +1 -0
- package/dist/services/operate/evidence-classifications.js +132 -0
- package/dist/services/operate/evidence-classifications.js.map +1 -0
- package/dist/services/operate/evidence-diagnostics.d.ts +23 -0
- package/dist/services/operate/evidence-diagnostics.d.ts.map +1 -0
- package/dist/services/operate/evidence-diagnostics.js +152 -0
- package/dist/services/operate/evidence-diagnostics.js.map +1 -0
- package/dist/services/operate/evidence.d.ts.map +1 -1
- package/dist/services/operate/evidence.js +54 -7
- package/dist/services/operate/evidence.js.map +1 -1
- package/dist/services/operate/index.d.ts.map +1 -1
- package/dist/services/operate/index.js +459 -62
- package/dist/services/operate/index.js.map +1 -1
- package/dist/services/operate/interaction/action-service.d.ts +17 -0
- package/dist/services/operate/interaction/action-service.d.ts.map +1 -0
- package/dist/services/operate/interaction/action-service.js +84 -0
- package/dist/services/operate/interaction/action-service.js.map +1 -0
- package/dist/services/operate/interaction/answer-service.d.ts +33 -0
- package/dist/services/operate/interaction/answer-service.d.ts.map +1 -0
- package/dist/services/operate/interaction/answer-service.js +239 -0
- package/dist/services/operate/interaction/answer-service.js.map +1 -0
- package/dist/services/operate/interaction/charter-suggestions.d.ts +29 -0
- package/dist/services/operate/interaction/charter-suggestions.d.ts.map +1 -0
- package/dist/services/operate/interaction/charter-suggestions.js +104 -0
- package/dist/services/operate/interaction/charter-suggestions.js.map +1 -0
- package/dist/services/operate/interaction/confirmation-service.d.ts +30 -0
- package/dist/services/operate/interaction/confirmation-service.d.ts.map +1 -0
- package/dist/services/operate/interaction/confirmation-service.js +78 -0
- package/dist/services/operate/interaction/confirmation-service.js.map +1 -0
- package/dist/services/operate/interaction/question-engine.d.ts +39 -0
- package/dist/services/operate/interaction/question-engine.d.ts.map +1 -0
- package/dist/services/operate/interaction/question-engine.js +277 -0
- package/dist/services/operate/interaction/question-engine.js.map +1 -0
- package/dist/services/operate/interaction/question-registry.d.ts +16 -0
- package/dist/services/operate/interaction/question-registry.d.ts.map +1 -0
- package/dist/services/operate/interaction/question-registry.js +190 -0
- package/dist/services/operate/interaction/question-registry.js.map +1 -0
- package/dist/services/operate/interaction/session-service.d.ts +55 -0
- package/dist/services/operate/interaction/session-service.d.ts.map +1 -0
- package/dist/services/operate/interaction/session-service.js +272 -0
- package/dist/services/operate/interaction/session-service.js.map +1 -0
- package/dist/services/operate/interaction/terminal-renderer.d.ts +8 -0
- package/dist/services/operate/interaction/terminal-renderer.d.ts.map +1 -0
- package/dist/services/operate/interaction/terminal-renderer.js +103 -0
- package/dist/services/operate/interaction/terminal-renderer.js.map +1 -0
- package/dist/services/operate/maintenance.d.ts.map +1 -1
- package/dist/services/operate/maintenance.js +40 -3
- package/dist/services/operate/maintenance.js.map +1 -1
- package/dist/services/operate/types.d.ts +194 -1
- package/dist/services/operate/types.d.ts.map +1 -1
- package/dist/services/operate/types.js.map +1 -1
- package/dist/services/operate/workspace.d.ts +1 -0
- package/dist/services/operate/workspace.d.ts.map +1 -1
- package/dist/services/operate/workspace.js +2 -0
- package/dist/services/operate/workspace.js.map +1 -1
- package/dist/services/pipeline-package-service.d.ts +14 -0
- package/dist/services/pipeline-package-service.d.ts.map +1 -1
- package/dist/services/pipeline-package-service.js +45 -1
- package/dist/services/pipeline-package-service.js.map +1 -1
- package/dist/services/runtime-manager-service.d.ts +3 -0
- package/dist/services/runtime-manager-service.d.ts.map +1 -1
- package/dist/services/runtime-manager-service.js +26 -2
- package/dist/services/runtime-manager-service.js.map +1 -1
- package/package.json +3 -2
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import {
|
|
3
|
+
import { canonicalDigest, sha256Digest } from './canonical.js';
|
|
4
|
+
import { applyOperatingInitialization, getOperatingProfile, listOperatingProfiles, normalizeCustomOperatingProfile, normalizeOperatingInitializationAnswers, prepareOperatingInitialization, validateOperatingConfiguration, } from './config.js';
|
|
4
5
|
import { runOperatingCycle } from './engine.js';
|
|
5
6
|
import { OperatingEventStore } from './event-store.js';
|
|
7
|
+
import { classifyEvidenceDiagnostic } from './evidence-classifications.js';
|
|
8
|
+
import { listEvidenceDiagnostics, readEvidenceDiagnostic } from './evidence-diagnostics.js';
|
|
6
9
|
import { parseStrictJson, readImportedEvidenceFile } from './evidence-import.js';
|
|
10
|
+
import { createOperatingAction } from './interaction/action-service.js';
|
|
11
|
+
import { persistableOperatingInitAnswers, resumeGuidedSession, submitGuidedAnswers, } from './interaction/answer-service.js';
|
|
12
|
+
import { assertOperatingConfirmation } from './interaction/confirmation-service.js';
|
|
13
|
+
import { createOperatingInitQuestionnaire, evaluateOperatingInitQuestions, operatingInitAnswersFromOptions, } from './interaction/question-engine.js';
|
|
14
|
+
import { cancelGuidedSession, createGuidedSession, createGuidedSessionId, currentGuidedSessionBindings, updateGuidedSession, } from './interaction/session-service.js';
|
|
7
15
|
import { assertCommittedOperatingView } from './journal.js';
|
|
8
16
|
import { applyOperatingMigration, inspectOperatingMigration, rollbackOperatingMigration, } from './legacy-import-service.js';
|
|
9
17
|
import { answerOperatingGap, applyOrRollbackRoute, decideOperatingDecision, governOperatingFinding, readOperatingCollection, readOperatingReview, transitionOperatingCycle, verifyOperatingGap, } from './lifecycle.js';
|
|
@@ -122,11 +130,16 @@ const OPERATE_EXIT_CODES = {
|
|
|
122
130
|
E_OPERATE_CHARTER_INCOMPLETE: 2,
|
|
123
131
|
E_OPERATE_CONFIG_INVALID: 2,
|
|
124
132
|
E_OPERATE_INPUT_TOO_LARGE: 2,
|
|
133
|
+
E_OPERATE_QUESTIONNAIRE_INVALID: 2,
|
|
125
134
|
E_OPERATE_PATH_ESCAPE: 2,
|
|
126
135
|
E_OPERATE_NOT_INITIALIZED: 3,
|
|
127
136
|
E_OPERATE_PROJECT_REQUIRED: 3,
|
|
128
137
|
E_PIPELINE_NOT_INSTALLED: 3,
|
|
138
|
+
E_PIPELINE_VERSION_INCOMPATIBLE: 3,
|
|
129
139
|
E_OPERATE_AUTHORITY_REQUIRED: 4,
|
|
140
|
+
E_OPERATE_INPUT_REQUIRED: 4,
|
|
141
|
+
E_OPERATE_SESSION_CANCELLED: 4,
|
|
142
|
+
E_OPERATE_SESSION_EXPIRED: 4,
|
|
130
143
|
E_OPERATE_ROUTE_CONFIRMATION_REQUIRED: 4,
|
|
131
144
|
E_OPERATE_CHECKPOINT_INVALID: 5,
|
|
132
145
|
E_OPERATE_CYCLE_ACTIVE: 5,
|
|
@@ -142,6 +155,8 @@ const OPERATE_EXIT_CODES = {
|
|
|
142
155
|
E_OPERATE_STALE_LOCK_UNSAFE: 5,
|
|
143
156
|
E_OPERATE_STATE_INVALID: 5,
|
|
144
157
|
E_OPERATE_TRANSACTION_INVALID: 5,
|
|
158
|
+
E_OPERATE_SESSION_REPLAY_CONFLICT: 5,
|
|
159
|
+
E_OPERATE_SESSION_STALE: 5,
|
|
145
160
|
E_OPERATE_ADVISOR_FAILED: 6,
|
|
146
161
|
E_OPERATE_ADVISOR_ISOLATION: 6,
|
|
147
162
|
E_OPERATE_CAP_EXCEEDED: 6,
|
|
@@ -151,41 +166,46 @@ const OPERATE_EXIT_CODES = {
|
|
|
151
166
|
E_OPERATE_EVIDENCE_REJECTED: 6,
|
|
152
167
|
E_OPERATE_PROVIDER_READ_ONLY: 6,
|
|
153
168
|
E_OPERATE_SECRET_DETECTED: 6,
|
|
169
|
+
E_OPERATE_SESSION_INVALID: 2,
|
|
154
170
|
};
|
|
155
171
|
function operateExitCode(code) {
|
|
156
172
|
return OPERATE_EXIT_CODES[code];
|
|
157
173
|
}
|
|
158
174
|
function failure(action, error) {
|
|
159
175
|
const code = error instanceof OperateError ? error.code : 'E_OPERATE_INTERNAL';
|
|
160
|
-
const
|
|
161
|
-
? [
|
|
162
|
-
:
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
? ['planr operate
|
|
176
|
-
: code === '
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
:
|
|
176
|
+
const explicitRecovery = error instanceof OperateError && typeof error.details?.recoveryCommand === 'string'
|
|
177
|
+
? [error.details.recoveryCommand]
|
|
178
|
+
: null;
|
|
179
|
+
const nextActions = explicitRecovery ??
|
|
180
|
+
(code === 'E_PIPELINE_NOT_INSTALLED'
|
|
181
|
+
? ['npm install -g openplanr@latest', 'planr setup --scope user', 'planr operate inspect']
|
|
182
|
+
: code === 'E_OPERATE_NOT_INITIALIZED'
|
|
183
|
+
? ['planr operate init']
|
|
184
|
+
: code === 'E_OPERATE_PROJECT_REQUIRED'
|
|
185
|
+
? ['cd /path/to/your/project', 'planr init', 'planr operate inspect']
|
|
186
|
+
: code === 'E_OPERATE_CONFIG_INVALID' || code === 'E_OPERATE_CHARTER_INCOMPLETE'
|
|
187
|
+
? ['planr operate config validate']
|
|
188
|
+
: code === 'E_OPERATE_ROUTE_CONFIRMATION_REQUIRED'
|
|
189
|
+
? ['planr operate routes apply <route-id> --preview --json']
|
|
190
|
+
: code === 'E_OPERATE_MIGRATION_CONFLICT'
|
|
191
|
+
? ['planr operate migrate inspect', 'planr operate migrations list']
|
|
192
|
+
: code === 'E_OPERATE_HEAD_DIVERGED' ||
|
|
193
|
+
code === 'E_OPERATE_CHECKPOINT_INVALID' ||
|
|
194
|
+
code === 'E_OPERATE_PROJECTION_DRIFT'
|
|
195
|
+
? ['planr operate integrity status', 'planr operate cycles recover <cycle-id>']
|
|
196
|
+
: code === 'E_OPERATE_CYCLE_ACTIVE' || code === 'E_OPERATE_CYCLE_INPUT_CONFLICT'
|
|
197
|
+
? ['planr operate status', 'planr operate cycles resume <cycle-id>']
|
|
198
|
+
: code === 'E_OPERATE_CYCLE_NOT_DISPOSED'
|
|
199
|
+
? [
|
|
200
|
+
'planr operate findings list',
|
|
201
|
+
'planr operate decisions list',
|
|
202
|
+
'planr operate cycles close <cycle-id>',
|
|
203
|
+
]
|
|
204
|
+
: code.startsWith('E_OPERATE_EVIDENCE') ||
|
|
205
|
+
code.startsWith('E_OPERATE_PROVIDER') ||
|
|
206
|
+
code.startsWith('E_OPERATE_ADVISOR')
|
|
207
|
+
? ['planr operate sources list', 'planr operate run --preview --json']
|
|
208
|
+
: ['planr operate diagnostics export']);
|
|
189
209
|
return {
|
|
190
210
|
schemaVersion: '1.0.0',
|
|
191
211
|
protocolVersion: '1.2.0',
|
|
@@ -205,6 +225,100 @@ function failure(action, error) {
|
|
|
205
225
|
exitCode: operateExitCode(code),
|
|
206
226
|
};
|
|
207
227
|
}
|
|
228
|
+
function publicActionCommands(nextActions) {
|
|
229
|
+
return [
|
|
230
|
+
...new Set(nextActions
|
|
231
|
+
.flatMap((value) => value.split(/\s+&&\s+/))
|
|
232
|
+
.map((value) => value
|
|
233
|
+
.trim()
|
|
234
|
+
.replace(/\s+--yes(?=\s|$)/g, '')
|
|
235
|
+
.replace(/\s+--confirm\s+\S+/g, '')
|
|
236
|
+
.replace(/\s+--preview-digest\s+\S+/g, ''))
|
|
237
|
+
.filter((value) => /^planr\s+/.test(value))),
|
|
238
|
+
];
|
|
239
|
+
}
|
|
240
|
+
function actionEffect(command) {
|
|
241
|
+
if (/\b(?:inspect|status|brief|review|list|show)\b/.test(command) ||
|
|
242
|
+
/\bconfig validate\b/.test(command) ||
|
|
243
|
+
/\bprofiles validate\b/.test(command) ||
|
|
244
|
+
/\bsources test\b/.test(command) ||
|
|
245
|
+
/\brun\b.*\b--preview\b/.test(command) ||
|
|
246
|
+
/\bmigrate inspect\b/.test(command) ||
|
|
247
|
+
/\bcache status\b/.test(command) ||
|
|
248
|
+
/\bintegrity status\b/.test(command)) {
|
|
249
|
+
return 'read-only';
|
|
250
|
+
}
|
|
251
|
+
if (/\boperate run\b/.test(command) && !/\b--offline\b/.test(command)) {
|
|
252
|
+
return 'provider-call';
|
|
253
|
+
}
|
|
254
|
+
if (/\b(?:adapter (?:prepare|record|resume|finalize|cancel)|cache purge|diagnostics export)\b/.test(command)) {
|
|
255
|
+
return 'machine-local-write';
|
|
256
|
+
}
|
|
257
|
+
if (/\bpipeline (?:plan|ship)\b/.test(command))
|
|
258
|
+
return 'external-effect';
|
|
259
|
+
return 'project-write';
|
|
260
|
+
}
|
|
261
|
+
async function attachStructuredActions(request, result) {
|
|
262
|
+
if (result.actions?.length || result.action === 'input_required')
|
|
263
|
+
return result;
|
|
264
|
+
const commands = publicActionCommands(result.nextActions);
|
|
265
|
+
if (commands.length === 0)
|
|
266
|
+
return result;
|
|
267
|
+
// Planning-only installations intentionally omit the portable pipeline and
|
|
268
|
+
// therefore its Protocol v1.2 action validators. Provider-free inspection
|
|
269
|
+
// and demonstration must still work; their legacy nextActions remain the
|
|
270
|
+
// compatible fallback until the full package is installed.
|
|
271
|
+
if (!resolveOperatingPipelineRoot())
|
|
272
|
+
return result;
|
|
273
|
+
const bindings = await currentGuidedSessionBindings(request.projectRoot);
|
|
274
|
+
const eventHead = await new OperatingEventStore(request.projectRoot)
|
|
275
|
+
.replay()
|
|
276
|
+
.then((value) => (value.eventHead.hash ? value.eventHead : null))
|
|
277
|
+
.catch(() => null);
|
|
278
|
+
const requestDigest = canonicalDigest({
|
|
279
|
+
action: request.action,
|
|
280
|
+
arguments: request.arguments ?? {},
|
|
281
|
+
options: Object.fromEntries(Object.entries(request.options)
|
|
282
|
+
.filter(([key]) => !['yes', 'confirm', 'stdin'].includes(key))
|
|
283
|
+
.map(([key, value]) => [key, canonicalDigest({ value })])),
|
|
284
|
+
});
|
|
285
|
+
const sessionId = `GIS-action-${requestDigest.slice('sha256:'.length, 'sha256:'.length + 24)}`;
|
|
286
|
+
const actions = [];
|
|
287
|
+
for (const [index, command] of commands.entries()) {
|
|
288
|
+
const effect = actionEffect(command);
|
|
289
|
+
const id = `operate.next.${canonicalDigest({ command }).slice('sha256:'.length, 'sha256:'.length + 20)}`.toLowerCase();
|
|
290
|
+
const created = await createOperatingAction({
|
|
291
|
+
id,
|
|
292
|
+
label: (() => {
|
|
293
|
+
const publicLabel = command.replace(/^planr\s+/, '');
|
|
294
|
+
return publicLabel.length <= 160
|
|
295
|
+
? publicLabel
|
|
296
|
+
: `${publicLabel.slice(0, 157).trimEnd()}...`;
|
|
297
|
+
})(),
|
|
298
|
+
description: `Run the named ${effect} action returned by ${request.action}.`,
|
|
299
|
+
command,
|
|
300
|
+
effect,
|
|
301
|
+
recommended: index === 0,
|
|
302
|
+
...(effect === 'read-only'
|
|
303
|
+
? {}
|
|
304
|
+
: {
|
|
305
|
+
confirmation: {
|
|
306
|
+
sessionId,
|
|
307
|
+
confirmationScope: `${request.action}:${index + 1}`,
|
|
308
|
+
projectIdentity: bindings.projectIdentity,
|
|
309
|
+
projectHead: bindings.projectHead,
|
|
310
|
+
configHead: bindings.configHead,
|
|
311
|
+
eventHead,
|
|
312
|
+
arguments: [requestDigest],
|
|
313
|
+
destinations: [],
|
|
314
|
+
writes: [],
|
|
315
|
+
},
|
|
316
|
+
}),
|
|
317
|
+
});
|
|
318
|
+
actions.push(created.action);
|
|
319
|
+
}
|
|
320
|
+
return { ...result, actions };
|
|
321
|
+
}
|
|
208
322
|
async function inspect(request) {
|
|
209
323
|
const pipelineRoot = resolveOperatingPipelineRoot();
|
|
210
324
|
const paths = resolveOperatingPaths(request.projectRoot);
|
|
@@ -324,75 +438,284 @@ function demo(request) {
|
|
|
324
438
|
next: ['planr operate init'],
|
|
325
439
|
});
|
|
326
440
|
}
|
|
441
|
+
async function initializationApplyAction(input) {
|
|
442
|
+
const sessionId = input.sessionId ??
|
|
443
|
+
`GIS-init-${input.preview.previewDigest.slice('sha256:'.length, 'sha256:'.length + 24)}`;
|
|
444
|
+
const command = input.sessionId
|
|
445
|
+
? `planr operate init --resume ${input.sessionId}`
|
|
446
|
+
: `planr operate init --preview-created-at ${input.preview.workspace.capturedAt}`;
|
|
447
|
+
return createOperatingAction({
|
|
448
|
+
id: 'operate.init.apply',
|
|
449
|
+
label: 'Apply Operating Board configuration',
|
|
450
|
+
description: 'Write only the reviewed charter, workspace, configuration, and machine-local preferences.',
|
|
451
|
+
command,
|
|
452
|
+
effect: 'project-write',
|
|
453
|
+
recommended: true,
|
|
454
|
+
confirmation: {
|
|
455
|
+
sessionId,
|
|
456
|
+
confirmationScope: 'operate.init.apply',
|
|
457
|
+
projectIdentity: input.bindings.projectIdentity,
|
|
458
|
+
projectHead: input.bindings.projectHead,
|
|
459
|
+
configHead: input.bindings.configHead,
|
|
460
|
+
eventHead: input.preview.expectedEventHead.hash ? input.preview.expectedEventHead : null,
|
|
461
|
+
arguments: [
|
|
462
|
+
`answers=${canonicalDigest(input.answers)}`,
|
|
463
|
+
`preview=${input.preview.previewDigest}`,
|
|
464
|
+
],
|
|
465
|
+
destinations: [
|
|
466
|
+
...input.preview.changedPaths,
|
|
467
|
+
...(input.preview.preferencesChanged ? ['~/.planr/operate/preferences.json'] : []),
|
|
468
|
+
],
|
|
469
|
+
writes: input.preview.writes.map((write) => `${write.relativePath}:${write.operation ?? 'replace'}:${sha256Digest(write.content)}`),
|
|
470
|
+
},
|
|
471
|
+
});
|
|
472
|
+
}
|
|
327
473
|
async function initialize(request) {
|
|
328
474
|
await loadOperatingProtocol();
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
475
|
+
let supplied = normalizeOperatingInitializationAnswers(operatingInitAnswersFromOptions(request.options));
|
|
476
|
+
const resumeId = option(request, 'resume', undefined);
|
|
477
|
+
const localRoot = option(request, 'localRoot', undefined);
|
|
478
|
+
if (option(request, 'cancelSession', false)) {
|
|
479
|
+
if (!resumeId) {
|
|
480
|
+
throw new OperateError('E_OPERATE_SESSION_INVALID', '--cancel-session requires --resume <session-id>.');
|
|
481
|
+
}
|
|
482
|
+
return success(request.action, {
|
|
483
|
+
message: 'Guided initialization session cancelled.',
|
|
484
|
+
state: 'cancelled',
|
|
485
|
+
data: await cancelGuidedSession({
|
|
486
|
+
projectRoot: request.projectRoot,
|
|
487
|
+
sessionId: resumeId,
|
|
488
|
+
localRoot,
|
|
489
|
+
}),
|
|
490
|
+
next: ['planr operate init --json'],
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
if (request.stdin !== undefined && !resumeId) {
|
|
494
|
+
throw new OperateError('E_OPERATE_SESSION_INVALID', '--stdin requires --resume <session-id>.');
|
|
495
|
+
}
|
|
496
|
+
const bindings = await currentGuidedSessionBindings(request.projectRoot);
|
|
497
|
+
let resumedSession = null;
|
|
498
|
+
if (resumeId) {
|
|
499
|
+
resumedSession =
|
|
500
|
+
request.stdin === undefined
|
|
501
|
+
? await resumeGuidedSession({
|
|
502
|
+
projectRoot: request.projectRoot,
|
|
503
|
+
sessionId: resumeId,
|
|
504
|
+
bindings,
|
|
505
|
+
localRoot,
|
|
506
|
+
})
|
|
507
|
+
: await submitGuidedAnswers({
|
|
508
|
+
projectRoot: request.projectRoot,
|
|
509
|
+
sessionId: resumeId,
|
|
510
|
+
raw: request.stdin,
|
|
511
|
+
bindings,
|
|
512
|
+
localRoot,
|
|
513
|
+
});
|
|
514
|
+
supplied = normalizeOperatingInitializationAnswers(resumedSession.answers);
|
|
515
|
+
if (resumedSession.status === 'input-required') {
|
|
516
|
+
return {
|
|
517
|
+
schemaVersion: '1.0.0',
|
|
518
|
+
protocolVersion: '1.2.0',
|
|
519
|
+
ok: false,
|
|
520
|
+
action: 'input_required',
|
|
521
|
+
code: 'E_OPERATE_INPUT_REQUIRED',
|
|
522
|
+
message: 'Operating Board initialization needs explicit human input.',
|
|
523
|
+
state: resumedSession.session.state,
|
|
524
|
+
paths: {},
|
|
525
|
+
counts: {},
|
|
526
|
+
warnings: [],
|
|
527
|
+
nextActions: [],
|
|
528
|
+
next: [],
|
|
529
|
+
questionnaire: resumedSession.questionnaire,
|
|
530
|
+
exitCode: operateExitCode('E_OPERATE_INPUT_REQUIRED'),
|
|
531
|
+
};
|
|
336
532
|
}
|
|
337
533
|
}
|
|
338
|
-
const profile = option(request, 'profile', 'saas');
|
|
339
534
|
let customProfile;
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
if (!profileFile) {
|
|
343
|
-
throw new OperateError('E_OPERATE_CONFIG_INVALID', 'The custom profile requires --profile-file inside the project.');
|
|
344
|
-
}
|
|
345
|
-
customProfile = await readCustomOperatingProfile(request.projectRoot, profileFile);
|
|
535
|
+
if (supplied.profile === 'custom' && supplied.profileFile) {
|
|
536
|
+
customProfile = await readCustomOperatingProfile(request.projectRoot, supplied.profileFile);
|
|
346
537
|
}
|
|
347
|
-
else if (
|
|
538
|
+
else if (supplied.profile !== undefined &&
|
|
539
|
+
supplied.profile !== 'custom' &&
|
|
540
|
+
supplied.profileFile) {
|
|
348
541
|
throw new OperateError('E_OPERATE_CONFIG_INVALID', '--profile-file is valid only with --profile custom.');
|
|
349
542
|
}
|
|
350
|
-
const
|
|
351
|
-
|
|
352
|
-
|
|
543
|
+
const context = {
|
|
544
|
+
projectRoot: request.projectRoot,
|
|
545
|
+
...bindings,
|
|
546
|
+
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
547
|
+
availableSources: ['repository', 'planr', 'git', 'file-import'],
|
|
548
|
+
runtime: option(request, 'runtime', 'unknown'),
|
|
549
|
+
interaction: request.interactive ? 'terminal' : 'none',
|
|
550
|
+
};
|
|
551
|
+
const questionState = await evaluateOperatingInitQuestions({
|
|
552
|
+
answers: supplied,
|
|
553
|
+
context,
|
|
554
|
+
requireCharter: true,
|
|
555
|
+
});
|
|
556
|
+
if (questionState.status === 'input-required') {
|
|
557
|
+
const sessionId = createGuidedSessionId();
|
|
558
|
+
const questionnaire = await createOperatingInitQuestionnaire({
|
|
559
|
+
context,
|
|
560
|
+
questions: questionState.questions,
|
|
561
|
+
stage: questionState.stage,
|
|
562
|
+
sessionId,
|
|
563
|
+
});
|
|
564
|
+
await createGuidedSession({
|
|
565
|
+
projectRoot: request.projectRoot,
|
|
566
|
+
questionnaire,
|
|
567
|
+
persistedAnswers: persistableOperatingInitAnswers(questionState.answers, context),
|
|
568
|
+
localRoot,
|
|
569
|
+
});
|
|
570
|
+
return {
|
|
571
|
+
schemaVersion: '1.0.0',
|
|
572
|
+
protocolVersion: '1.2.0',
|
|
573
|
+
ok: false,
|
|
574
|
+
action: 'input_required',
|
|
575
|
+
code: 'E_OPERATE_INPUT_REQUIRED',
|
|
576
|
+
message: 'Operating Board initialization needs explicit human input.',
|
|
577
|
+
state: null,
|
|
578
|
+
paths: {},
|
|
579
|
+
counts: {},
|
|
580
|
+
warnings: [],
|
|
581
|
+
nextActions: [],
|
|
582
|
+
next: [],
|
|
583
|
+
questionnaire,
|
|
584
|
+
exitCode: operateExitCode('E_OPERATE_INPUT_REQUIRED'),
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
const profile = supplied.profile;
|
|
588
|
+
const decisionOwner = supplied.decisionOwner ?? option(request, 'decisionOwner', '').trim();
|
|
589
|
+
const planningEngine = supplied.planningEngine ??
|
|
590
|
+
option(request, 'planningEngine', 'openplanr');
|
|
591
|
+
const rawCharter = supplied.charter ?? option(request, 'charter', {});
|
|
353
592
|
const preview = await prepareOperatingInitialization({
|
|
354
593
|
projectRoot: request.projectRoot,
|
|
355
594
|
profile,
|
|
356
595
|
decisionOwner,
|
|
357
596
|
planningEngine,
|
|
358
|
-
runtime: option(request, 'runtime', 'auto'),
|
|
359
|
-
cadence: option(request, 'cadence', 'manual'),
|
|
360
|
-
timezone:
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
597
|
+
runtime: supplied.runtime ?? option(request, 'runtime', 'auto'),
|
|
598
|
+
cadence: supplied.cadence ?? option(request, 'cadence', 'manual'),
|
|
599
|
+
timezone: supplied.timezone ??
|
|
600
|
+
option(request, 'timezone', Intl.DateTimeFormat().resolvedOptions().timeZone),
|
|
601
|
+
sensitivityCeiling: supplied.sensitivityCeiling ?? option(request, 'sensitivityCeiling', 'internal'),
|
|
602
|
+
enabledProviders: (supplied.sources ?? stringList(request.options.sources)).length
|
|
603
|
+
? (supplied.sources ?? stringList(request.options.sources))
|
|
364
604
|
: undefined,
|
|
365
|
-
evidenceFiles: stringList(request.options.evidenceFile),
|
|
605
|
+
evidenceFiles: supplied.evidenceFiles ?? stringList(request.options.evidenceFile),
|
|
366
606
|
charter: rawCharter,
|
|
367
607
|
customProfile,
|
|
368
|
-
componentRoots: stringList(request.options.components),
|
|
608
|
+
componentRoots: supplied.componentRoots ?? stringList(request.options.components),
|
|
609
|
+
localRoot,
|
|
610
|
+
now: resumedSession?.session.createdAt ??
|
|
611
|
+
option(request, 'previewCreatedAt', undefined),
|
|
369
612
|
});
|
|
370
613
|
const legacyMigration = await inspectOperatingMigration({
|
|
371
614
|
projectRoot: request.projectRoot,
|
|
372
615
|
});
|
|
373
616
|
const previewData = {
|
|
374
617
|
previewDigest: preview.previewDigest,
|
|
618
|
+
expectedEventHead: preview.expectedEventHead,
|
|
619
|
+
resultingEventHead: preview.resultingEventHead,
|
|
375
620
|
changedPaths: preview.changedPaths,
|
|
376
621
|
localPreferencesChanged: preview.preferencesChanged,
|
|
622
|
+
previewCreatedAt: preview.workspace.capturedAt,
|
|
377
623
|
workspaceDigest: preview.workspace.workspaceDigest,
|
|
378
624
|
config: preview.config,
|
|
379
625
|
legacyMigration,
|
|
626
|
+
...(resumedSession ? { sessionId: resumedSession.session.sessionId } : {}),
|
|
380
627
|
};
|
|
628
|
+
const { action: applyAction, confirmation } = await initializationApplyAction({
|
|
629
|
+
request,
|
|
630
|
+
preview,
|
|
631
|
+
bindings,
|
|
632
|
+
sessionId: resumedSession?.session.sessionId,
|
|
633
|
+
answers: questionState.answers,
|
|
634
|
+
});
|
|
635
|
+
const requestedConfirmation = option(request, 'confirm', undefined);
|
|
636
|
+
const confirmed = option(request, 'yes', false);
|
|
637
|
+
if (resumedSession) {
|
|
638
|
+
await updateGuidedSession({
|
|
639
|
+
projectRoot: request.projectRoot,
|
|
640
|
+
localRoot,
|
|
641
|
+
session: {
|
|
642
|
+
...resumedSession.session,
|
|
643
|
+
state: 'preview-ready',
|
|
644
|
+
previewDigest: preview.previewDigest,
|
|
645
|
+
confirmationDigest: confirmation?.confirmationDigest,
|
|
646
|
+
updatedAt: new Date().toISOString(),
|
|
647
|
+
},
|
|
648
|
+
});
|
|
649
|
+
if (!confirmed || !requestedConfirmation) {
|
|
650
|
+
return success(request.action, {
|
|
651
|
+
message: 'Operating Board initialization preview is ready; no state was written.',
|
|
652
|
+
state: 'preview-ready',
|
|
653
|
+
preview: { ...previewData, confirmation },
|
|
654
|
+
actions: [applyAction],
|
|
655
|
+
next: [
|
|
656
|
+
`planr operate init --resume ${resumedSession.session.sessionId} --confirm ${confirmation?.confirmationDigest} --yes --json`,
|
|
657
|
+
],
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
}
|
|
381
661
|
if (option(request, 'preview', false) || option(request, 'dryRun', false)) {
|
|
382
662
|
return success(request.action, {
|
|
383
663
|
message: 'Operating Board initialization preview is ready; no state was written.',
|
|
384
|
-
|
|
385
|
-
|
|
664
|
+
state: 'preview-ready',
|
|
665
|
+
preview: { ...previewData, confirmation },
|
|
666
|
+
actions: [applyAction],
|
|
667
|
+
next: [
|
|
668
|
+
`planr operate init --preview-created-at ${preview.workspace.capturedAt} --confirm ${confirmation?.confirmationDigest} --yes`,
|
|
669
|
+
],
|
|
386
670
|
});
|
|
387
671
|
}
|
|
388
|
-
if (!
|
|
389
|
-
throw new OperateError('E_OPERATE_ROUTE_CONFIRMATION_REQUIRED', '
|
|
672
|
+
if (!confirmed || !requestedConfirmation || !confirmation) {
|
|
673
|
+
throw new OperateError('E_OPERATE_ROUTE_CONFIRMATION_REQUIRED', 'Initialization requires the exact named confirmation returned by its preview.', {
|
|
674
|
+
preview: { ...previewData, confirmation },
|
|
675
|
+
action: applyAction,
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
const accepted = assertOperatingConfirmation({
|
|
679
|
+
expected: confirmation,
|
|
680
|
+
actionId: applyAction.id,
|
|
681
|
+
confirmationDigest: requestedConfirmation,
|
|
682
|
+
confirmed,
|
|
683
|
+
});
|
|
684
|
+
if (resumedSession) {
|
|
685
|
+
await updateGuidedSession({
|
|
686
|
+
projectRoot: request.projectRoot,
|
|
687
|
+
localRoot,
|
|
688
|
+
session: {
|
|
689
|
+
...resumedSession.session,
|
|
690
|
+
state: 'confirmed',
|
|
691
|
+
previewDigest: preview.previewDigest,
|
|
692
|
+
confirmationDigest: accepted.confirmationDigest,
|
|
693
|
+
confirmedAt: accepted.confirmedAt,
|
|
694
|
+
updatedAt: accepted.confirmedAt ?? new Date().toISOString(),
|
|
695
|
+
},
|
|
696
|
+
});
|
|
390
697
|
}
|
|
391
698
|
const applied = await applyOperatingInitialization({
|
|
392
699
|
projectRoot: request.projectRoot,
|
|
393
700
|
preview,
|
|
394
701
|
confirmationDigest: preview.previewDigest,
|
|
395
702
|
});
|
|
703
|
+
if (resumedSession) {
|
|
704
|
+
const appliedAt = new Date().toISOString();
|
|
705
|
+
await updateGuidedSession({
|
|
706
|
+
projectRoot: request.projectRoot,
|
|
707
|
+
localRoot,
|
|
708
|
+
session: {
|
|
709
|
+
...resumedSession.session,
|
|
710
|
+
state: 'applied',
|
|
711
|
+
previewDigest: preview.previewDigest,
|
|
712
|
+
confirmationDigest: accepted.confirmationDigest,
|
|
713
|
+
confirmedAt: accepted.confirmedAt,
|
|
714
|
+
appliedAt,
|
|
715
|
+
updatedAt: appliedAt,
|
|
716
|
+
},
|
|
717
|
+
});
|
|
718
|
+
}
|
|
396
719
|
return success(request.action, {
|
|
397
720
|
message: applied.initialized
|
|
398
721
|
? 'Operating Board initialized.'
|
|
@@ -539,6 +862,7 @@ async function status(request) {
|
|
|
539
862
|
});
|
|
540
863
|
}
|
|
541
864
|
async function run(request) {
|
|
865
|
+
await validateOperatingConfiguration(request.projectRoot);
|
|
542
866
|
const requestedRuntime = option(request, 'runtime', 'auto');
|
|
543
867
|
const deferAdvisors = !option(request, 'offline', false) &&
|
|
544
868
|
!option(request, 'dryRun', false) &&
|
|
@@ -635,6 +959,71 @@ async function collections(request) {
|
|
|
635
959
|
}
|
|
636
960
|
return success(request.action, { data });
|
|
637
961
|
}
|
|
962
|
+
async function evidenceRecovery(request) {
|
|
963
|
+
const candidateId = argument(request, 'candidateId');
|
|
964
|
+
if (request.action === 'evidence.diagnose') {
|
|
965
|
+
const data = candidateId
|
|
966
|
+
? await readEvidenceDiagnostic({
|
|
967
|
+
projectRoot: request.projectRoot,
|
|
968
|
+
candidateId,
|
|
969
|
+
localRoot: option(request, 'localRoot', undefined),
|
|
970
|
+
})
|
|
971
|
+
: await listEvidenceDiagnostics({
|
|
972
|
+
projectRoot: request.projectRoot,
|
|
973
|
+
localRoot: option(request, 'localRoot', undefined),
|
|
974
|
+
});
|
|
975
|
+
const diagnostic = Array.isArray(data) ? null : data;
|
|
976
|
+
return success(request.action, {
|
|
977
|
+
data: {
|
|
978
|
+
diagnostic: data,
|
|
979
|
+
valueDisclosed: false,
|
|
980
|
+
recovery: diagnostic
|
|
981
|
+
? {
|
|
982
|
+
repairOrRemove: diagnostic.location
|
|
983
|
+
? `Repair or remove the candidate at ${diagnostic.location}${diagnostic.line ? `:${diagnostic.line}` : ''}, then rerun.`
|
|
984
|
+
: 'Repair or remove the candidate in the identified component, then rerun.',
|
|
985
|
+
rotateCredential: 'If this is a real credential, rotate it before removing it from source history.',
|
|
986
|
+
eligibleExclusion: 'Exclude only the exact eligible source path through operating source policy; broad scanner bypasses are not supported.',
|
|
987
|
+
falsePositive: `planr operate evidence classify ${diagnostic.candidateId} --status false-positive --reason "<reason>" --json`,
|
|
988
|
+
rerun: 'planr operate run --offline',
|
|
989
|
+
}
|
|
990
|
+
: null,
|
|
991
|
+
},
|
|
992
|
+
actions: diagnostic?.actions,
|
|
993
|
+
next: diagnostic
|
|
994
|
+
? [
|
|
995
|
+
`planr operate evidence classify ${diagnostic.candidateId} --status false-positive --reason "<reason>" --json`,
|
|
996
|
+
'planr operate run --offline',
|
|
997
|
+
]
|
|
998
|
+
: [],
|
|
999
|
+
});
|
|
1000
|
+
}
|
|
1001
|
+
if (!candidateId) {
|
|
1002
|
+
throw new OperateError('E_OPERATE_CONFIG_INVALID', 'Evidence candidate ID is required.');
|
|
1003
|
+
}
|
|
1004
|
+
const status = option(request, 'status', '');
|
|
1005
|
+
if (status !== 'false-positive' && status !== 'confirmed-secret') {
|
|
1006
|
+
throw new OperateError('E_OPERATE_CONFIG_INVALID', 'Evidence classification status must be false-positive or confirmed-secret.');
|
|
1007
|
+
}
|
|
1008
|
+
const config = await validateOperatingConfiguration(request.projectRoot);
|
|
1009
|
+
const data = await classifyEvidenceDiagnostic({
|
|
1010
|
+
projectRoot: request.projectRoot,
|
|
1011
|
+
candidateId,
|
|
1012
|
+
status,
|
|
1013
|
+
reason: option(request, 'reason', ''),
|
|
1014
|
+
classifiedBy: config.decisionOwner,
|
|
1015
|
+
confirmationDigest: option(request, 'confirm', undefined),
|
|
1016
|
+
confirmed: option(request, 'yes', false),
|
|
1017
|
+
localRoot: option(request, 'localRoot', undefined),
|
|
1018
|
+
});
|
|
1019
|
+
return success(request.action, {
|
|
1020
|
+
data,
|
|
1021
|
+
actions: data.state === 'preview' && data.action ? [data.action] : undefined,
|
|
1022
|
+
message: data.state === 'classified'
|
|
1023
|
+
? `Evidence candidate ${candidateId} was classified without storing or exposing its value.`
|
|
1024
|
+
: 'Review and confirm the exact evidence classification action.',
|
|
1025
|
+
});
|
|
1026
|
+
}
|
|
638
1027
|
async function cycleMutation(request) {
|
|
639
1028
|
const cycleId = argument(request, 'cycleId');
|
|
640
1029
|
if (!cycleId)
|
|
@@ -736,6 +1125,7 @@ async function maintenance(request) {
|
|
|
736
1125
|
projectRoot: request.projectRoot,
|
|
737
1126
|
action: request.action.endsWith('purge') ? 'purge' : 'status',
|
|
738
1127
|
confirmed: option(request, 'yes', false),
|
|
1128
|
+
localRoot: option(request, 'localRoot', undefined),
|
|
739
1129
|
}),
|
|
740
1130
|
});
|
|
741
1131
|
}
|
|
@@ -844,6 +1234,8 @@ const HANDLERS = {
|
|
|
844
1234
|
'gaps.verify': answerMutation,
|
|
845
1235
|
'evidence.list': collections,
|
|
846
1236
|
'evidence.show': collections,
|
|
1237
|
+
'evidence.diagnose': evidenceRecovery,
|
|
1238
|
+
'evidence.classify': evidenceRecovery,
|
|
847
1239
|
'migrate.inspect': maintenance,
|
|
848
1240
|
'migrate.apply': maintenance,
|
|
849
1241
|
'migrations.list': collections,
|
|
@@ -872,10 +1264,15 @@ export async function executeOperateAction(request) {
|
|
|
872
1264
|
if (!handler) {
|
|
873
1265
|
throw new OperateError('E_OPERATE_ACTION_UNKNOWN', `Unknown Operating Board action: ${request.action}.`);
|
|
874
1266
|
}
|
|
875
|
-
return await handler(request);
|
|
1267
|
+
return await attachStructuredActions(request, await handler(request));
|
|
876
1268
|
}
|
|
877
1269
|
catch (error) {
|
|
878
|
-
|
|
1270
|
+
try {
|
|
1271
|
+
return await attachStructuredActions(request, failure(request.action, error));
|
|
1272
|
+
}
|
|
1273
|
+
catch {
|
|
1274
|
+
return failure(request.action, error);
|
|
1275
|
+
}
|
|
879
1276
|
}
|
|
880
1277
|
}
|
|
881
1278
|
//# sourceMappingURL=index.js.map
|