@zq-silk/yui 2.8.1 → 2.10.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/README.md +5 -3
- package/dist/cli/commandCatalog.js +56 -21
- package/dist/cli/helpRenderer.js +3 -1
- package/dist/cli/interactionPolicy.js +2 -2
- package/dist/commands/durableJobCommands.js +50 -0
- package/dist/commands/globalRoleCommands.js +30 -9
- package/dist/commands/inboxCommands.js +79 -22
- package/dist/commands/projectCommands.js +4 -33
- package/dist/commands/scheduleCommands.js +45 -0
- package/dist/commands/taskActivationCommands.js +19 -5
- package/dist/commands/taskCommands.js +8 -2
- package/dist/commands/taskFactCommands.js +24 -12
- package/dist/commands/taskProjectDeliveryCommand.js +56 -0
- package/dist/commands/taskPublicationCommands.js +6 -3
- package/dist/commands/taskPublicationVerifyCommand.js +74 -28
- package/dist/commands/taskRemoteDeliveryCommand.js +5 -3
- package/dist/context/runContextPack.js +25 -4
- package/dist/context/runInputContract.js +1 -0
- package/dist/context/taskContext.js +53 -16
- package/dist/context/taskPhase.js +99 -0
- package/dist/controlPlaneCli.js +38 -17
- package/dist/controller/controller.js +35 -0
- package/dist/controller/fileSchedulerStoreAdapter.js +75 -16
- package/dist/controller/globalInputDelivery.js +17 -7
- package/dist/controller/inboxTurnCheck.js +64 -0
- package/dist/controller/jobControl.js +42 -12
- package/dist/controller/jobDefinitionControl.js +304 -0
- package/dist/controller/jobSupervisor.js +47 -32
- package/dist/controller/providerRetryAdmission.js +2 -0
- package/dist/controller/runtime.js +11 -2
- package/dist/controller/scheduleControl.js +176 -0
- package/dist/coordination/workMailbox.js +3 -3
- package/dist/external/githubPublicationVerifier.js +14 -5
- package/dist/external/gitlabPublicationVerifier.js +12 -29
- package/dist/grant/taskAuthorization.js +7 -2
- package/dist/interaction/notificationPresentation.js +12 -3
- package/dist/interaction/operatorPresentation.js +5 -0
- package/dist/job/builtinJob.js +58 -0
- package/dist/job/durableJob.js +72 -49
- package/dist/job/jobDefinition.js +68 -0
- package/dist/job/jobRunner.js +2 -1
- package/dist/job/projectRefreshRunner.js +48 -0
- package/dist/message/globalInboxRecovery.js +44 -0
- package/dist/message/globalInterrupt.js +1 -1
- package/dist/message/inboxReceipt.js +14 -6
- package/dist/message/inboxTurn.js +86 -0
- package/dist/message/message.js +11 -0
- package/dist/message/taskInboxOwnership.js +20 -6
- package/dist/repository/projectRefresh.js +58 -0
- package/dist/repository/taskWorkspacePreparer.js +2 -1
- package/dist/repository/workspaceCleanupInspection.js +6 -1
- package/dist/schedule/schedule.js +108 -0
- package/dist/scheduler/operatorEvent.js +2 -0
- package/dist/scheduler/operatorInputNotificationProcessor.js +8 -0
- package/dist/storage/contextRecords.js +8 -0
- package/dist/storage/jobSchema.js +15 -0
- package/dist/storage/maintenanceCaller.js +1 -1
- package/dist/storage/recordValidation.js +5 -0
- package/dist/storage/scheduleSchema.js +12 -0
- package/dist/storage/sqliteSchema.js +95 -24
- package/dist/storage/sqliteStore.js +123 -10
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/storeRpc.js +5 -0
- package/dist/storage/upgrade/jobDefinitionMigration.js +22 -0
- package/dist/task/archiveDiagnostics.js +1 -1
- package/dist/task/archivePreflight.js +1 -1
- package/dist/task/projectDeliveryScope.js +20 -0
- package/dist/task/publicationToolObservation.js +126 -0
- package/dist/task/remoteDelivery.js +19 -8
- package/dist/task/remoteDeliveryService.js +5 -4
- package/dist/task/taskActivationService.js +35 -0
- package/dist/web/assets/client/app/settings.js +7 -1
- package/dist/web/assets/client/i18n/en.js +65 -2
- package/dist/web/assets/client/i18n/zh-CN.js +65 -2
- package/dist/web/assets/client/index.js +2 -0
- package/dist/web/assets/client/views/jobs.js +222 -0
- package/dist/web/assets/client/views/task/deliveryResults.js +6 -2
- package/dist/web/assets/client/views/task/page.js +11 -1
- package/dist/web/assets/shared/manageNav.js +1 -0
- package/dist/web/assets/shared/taskTabs.js +2 -2
- package/dist/web/assets/shell/settingsContent.js +3 -0
- package/dist/web/assets/styles/index.js +2 -0
- package/dist/web/assets/styles/views/jobs.js +19 -0
- package/dist/web/controllerWeb.js +73 -21
- package/dist/web/taskPreviews.js +21 -13
- package/dist/web/webAccess.js +109 -0
- package/dist/web/webJobs.js +52 -0
- package/dist/web/webServer.js +76 -44
- package/docs/architecture/capabilities-and-resources.md +6 -4
- package/docs/architecture/capabilities-and-resources.zh-CN.md +5 -4
- package/docs/cli-information-contract.md +57 -4
- package/docs/cli-information-contract.zh-CN.md +41 -2
- package/docs/job-runs.md +224 -0
- package/docs/project-delivery-scope.md +59 -0
- package/docs/release-workflow.md +86 -0
- package/docs/release-workflow.zh-CN.md +68 -0
- package/docs/schedules.md +132 -0
- package/docs/storage-baseline.md +31 -1
- package/docs/storage-baseline.zh-CN.md +25 -1
- package/docs/task-delivery.md +74 -2
- package/docs/task-delivery.zh-CN.md +39 -1
- package/docs/testing/verification-levels.md +6 -2
- package/docs/testing/verification-levels.zh-CN.md +6 -3
- package/i18n/README.zh-CN.md +6 -3
- package/package.json +4 -3
- package/skills/yui-leader/SKILL.md +44 -1
- package/skills/yui-leader/VERSION +1 -1
- package/skills/yui-leader/references/planning.md +9 -1
- package/skills/yui-operator/SKILL.md +31 -0
- package/skills/yui-operator/VERSION +1 -1
- package/skills/yui-reviewer/SKILL.md +4 -0
- package/skills/yui-reviewer/VERSION +1 -1
- package/skills/yui-runtime/SKILL.md +67 -8
- package/skills/yui-runtime/VERSION +1 -1
- package/skills/yui-runtime/references/context-ordering.md +103 -0
- package/skills/yui-runtime/references/publication.md +44 -4
- package/skills/yui-runtime/references/recovery.md +21 -0
- package/skills/yui-worker/SKILL.md +5 -0
- package/skills/yui-worker/VERSION +1 -1
|
@@ -4,8 +4,15 @@ import { upsertTaskPublication } from "./taskPublicationCommands.js";
|
|
|
4
4
|
import { dataError, runtimeError, taskNotFound, usageError, CliError } from "../errors/cliError.js";
|
|
5
5
|
import { publicationExternalKey } from "../task/publicationReference.js";
|
|
6
6
|
import { resolveTaskRecordReference } from "../task/taskRecordReference.js";
|
|
7
|
+
import { assertPublicationToolObservation, canonicalPublicationUrl, parsePublicationToolObservation, publicationObservationNotBefore } from "../task/publicationToolObservation.js";
|
|
7
8
|
export async function runTaskPublicationVerifyCommand(args, store, options) {
|
|
8
|
-
const
|
|
9
|
+
const observationIndex = args.indexOf("--observation");
|
|
10
|
+
if (observationIndex !== -1 && observationIndex !== args.length - 2) {
|
|
11
|
+
throw usageError("--observation must be followed by one JSON object at the end of the command.");
|
|
12
|
+
}
|
|
13
|
+
const supplied = observationIndex === -1 ? undefined
|
|
14
|
+
: parsePublicationToolObservation(args[observationIndex + 1]);
|
|
15
|
+
const reference = parsePublicationVerificationReference(observationIndex === -1 ? args : args.slice(0, observationIndex));
|
|
9
16
|
const task = store.getTask(reference.taskId);
|
|
10
17
|
if (task === null)
|
|
11
18
|
throw taskNotFound(reference.taskId);
|
|
@@ -13,31 +20,48 @@ export async function runTaskPublicationVerifyCommand(args, store, options) {
|
|
|
13
20
|
throw usageError(`Archived Task Publication cannot be verified: ${task.id}.`);
|
|
14
21
|
}
|
|
15
22
|
taskActor(options.environment, task.id);
|
|
16
|
-
const request = preparePublicationVerification(store, reference.taskId, reference.localId, options.environment);
|
|
17
|
-
const
|
|
18
|
-
if (verifier === undefined) {
|
|
19
|
-
throw usageError(`Publication verification is not supported for provider `
|
|
20
|
-
+ `${request.publication.provider}.`);
|
|
21
|
-
}
|
|
23
|
+
const request = preparePublicationVerification(store, reference.taskId, reference.localId, options.environment, supplied);
|
|
24
|
+
const now = options.now?.() ?? new Date();
|
|
22
25
|
let observation;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
catch (error) {
|
|
36
|
-
if (error instanceof CliError)
|
|
37
|
-
throw error;
|
|
38
|
-
throw runtimeError(`Publication verification failed for ${request.publication.provider}/`
|
|
39
|
-
+ `${request.publication.repository}/${request.publication.externalId}.`, { cause: error });
|
|
26
|
+
if (supplied !== undefined) {
|
|
27
|
+
if (request.publication.evidence === supplied.evidence
|
|
28
|
+
&& request.publication.supersedes === supplied.publicationId) {
|
|
29
|
+
return {
|
|
30
|
+
kind: "output",
|
|
31
|
+
output: `Tool observation already recorded: ${request.publication.id} (${request.publication.verification}).\n`,
|
|
32
|
+
data: { publication: request.publication, observation: supplied }
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
assertPublicationToolObservation(request.publication, supplied, now, request.observationNotBefore);
|
|
36
|
+
observation = supplied;
|
|
40
37
|
}
|
|
38
|
+
else
|
|
39
|
+
try {
|
|
40
|
+
const verifier = options.verifiers[request.publication.provider];
|
|
41
|
+
if (verifier === undefined) {
|
|
42
|
+
throw usageError(`No built-in verifier for ${request.publication.provider}; `
|
|
43
|
+
+ "use --observation with evidence from an authorized tool.");
|
|
44
|
+
}
|
|
45
|
+
observation = await verifier.inspect({
|
|
46
|
+
provider: request.publication.provider,
|
|
47
|
+
repository: request.publication.repository,
|
|
48
|
+
externalKind: request.publication.externalKind,
|
|
49
|
+
externalId: request.publication.externalId,
|
|
50
|
+
...(request.publication.externalUrl === undefined
|
|
51
|
+
? {}
|
|
52
|
+
: { externalUrl: request.publication.externalUrl }),
|
|
53
|
+
...(request.publication.targetBranch === undefined
|
|
54
|
+
? {}
|
|
55
|
+
: { targetBranch: request.publication.targetBranch }),
|
|
56
|
+
expectedLocalCommit: request.expectedLocalCommit
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
if (error instanceof CliError)
|
|
61
|
+
throw error;
|
|
62
|
+
throw runtimeError(`Publication verification failed for ${request.publication.provider}/`
|
|
63
|
+
+ `${request.publication.repository}/${request.publication.externalId}.`, { cause: error });
|
|
64
|
+
}
|
|
41
65
|
assertVerificationObservation(request, observation);
|
|
42
66
|
const result = commitPublicationVerification(store, request, observation, options.now?.() ?? new Date());
|
|
43
67
|
return {
|
|
@@ -54,7 +78,7 @@ export async function runTaskPublicationVerifyCommand(args, store, options) {
|
|
|
54
78
|
data: { publication: result.reference, observation }
|
|
55
79
|
};
|
|
56
80
|
}
|
|
57
|
-
function preparePublicationVerification(store, taskId, publicationId, environment) {
|
|
81
|
+
function preparePublicationVerification(store, taskId, publicationId, environment, supplied) {
|
|
58
82
|
return store.transaction((reader) => {
|
|
59
83
|
const task = reader.getTask(taskId);
|
|
60
84
|
if (task === null)
|
|
@@ -63,11 +87,18 @@ function preparePublicationVerification(store, taskId, publicationId, environmen
|
|
|
63
87
|
throw usageError(`Archived Task Publication cannot be verified: ${task.id}.`);
|
|
64
88
|
}
|
|
65
89
|
const actor = taskActor(environment, task.id);
|
|
66
|
-
|
|
90
|
+
let publication = reader.getPublicationReference(task.id, publicationId);
|
|
67
91
|
if (publication === null) {
|
|
68
92
|
throw dataError(`Publication reference not found: ${task.id}/${publicationId}.`);
|
|
69
93
|
}
|
|
70
94
|
const current = reader.findPublicationReferenceByExternalKey(publicationExternalKey(publication));
|
|
95
|
+
// Only the immediate immutable successor of this exact observation is a
|
|
96
|
+
// retry. A later observation/candidate must never revive an old receipt.
|
|
97
|
+
if (current?.taskId === task.id && current.supersedes === publication.id
|
|
98
|
+
&& supplied?.taskId === task.id && supplied.publicationId === publication.id
|
|
99
|
+
&& current.evidence === supplied.evidence) {
|
|
100
|
+
publication = current;
|
|
101
|
+
}
|
|
71
102
|
if (current === null || current.taskId !== task.id || current.id !== publication.id) {
|
|
72
103
|
throw usageError(`Publication ${publication.id} is not the current unsuperseded record `
|
|
73
104
|
+ `for ${publicationExternalKey(publication)}.`);
|
|
@@ -84,7 +115,9 @@ function preparePublicationVerification(store, taskId, publicationId, environmen
|
|
|
84
115
|
task: taskVerificationInvariant(task),
|
|
85
116
|
actor,
|
|
86
117
|
publication,
|
|
87
|
-
expectedLocalCommit: publication.localCommit
|
|
118
|
+
expectedLocalCommit: publication.localCommit,
|
|
119
|
+
observationNotBefore: supplied === undefined ? undefined
|
|
120
|
+
: publicationObservationNotBefore(publication, id => reader.getPublicationReference(task.id, id))
|
|
88
121
|
};
|
|
89
122
|
});
|
|
90
123
|
}
|
|
@@ -99,6 +132,18 @@ function assertVerificationObservation(request, observation) {
|
|
|
99
132
|
if (!/^[0-9a-f]{40}$/u.test(observation.headCommit)) {
|
|
100
133
|
throw usageError("Provider observation requires an exact remote head.");
|
|
101
134
|
}
|
|
135
|
+
if (publication.externalUrl !== undefined
|
|
136
|
+
&& (observation.externalUrl === undefined
|
|
137
|
+
|| canonicalPublicationUrl(observation.externalUrl, publication.provider)
|
|
138
|
+
!== canonicalPublicationUrl(publication.externalUrl, publication.provider))) {
|
|
139
|
+
throw usageError("Provider returned mismatched Publication service or URL.");
|
|
140
|
+
}
|
|
141
|
+
if (observation.targetBranch === undefined || observation.targetBranch.trim().length === 0) {
|
|
142
|
+
throw usageError("Provider observation requires an exact target branch.");
|
|
143
|
+
}
|
|
144
|
+
if (publication.targetBranch !== undefined && observation.targetBranch !== publication.targetBranch) {
|
|
145
|
+
throw usageError("Provider returned mismatched Publication target branch.");
|
|
146
|
+
}
|
|
102
147
|
if (observation.state === "merged" && observation.remoteCommit === undefined) {
|
|
103
148
|
throw usageError(`Merged remote ${publication.externalKind} did not expose a remote commit.`);
|
|
104
149
|
}
|
|
@@ -130,6 +175,7 @@ function commitPublicationVerification(store, request, observation, now) {
|
|
|
130
175
|
...(observation.externalUrl === undefined
|
|
131
176
|
? {}
|
|
132
177
|
: { externalUrl: observation.externalUrl }),
|
|
178
|
+
targetBranch: observation.targetBranch,
|
|
133
179
|
localCommit: request.expectedLocalCommit,
|
|
134
180
|
headCommit: observation.headCommit,
|
|
135
181
|
...(observation.remoteCommit === undefined ? {} : { remoteCommit: observation.remoteCommit }),
|
|
@@ -145,7 +191,7 @@ function commitPublicationVerification(store, request, observation, now) {
|
|
|
145
191
|
}
|
|
146
192
|
function parsePublicationVerificationReference(args) {
|
|
147
193
|
const usage = "Task publication verify usage: "
|
|
148
|
-
+ "yui task publication verify (<task>/<publication-id> | <task> <publication-id>).";
|
|
194
|
+
+ "yui task publication verify (<task>/<publication-id> | <task> <publication-id>) [--observation <json>].";
|
|
149
195
|
if (args.length !== 1 && args.length !== 2)
|
|
150
196
|
throw usageError(usage);
|
|
151
197
|
return resolveTaskRecordReference(args.length === 1 ? args[0] : `${args[0]}/${args[1]}`, { kind: "publicationReference", label: "Publication reference" });
|
|
@@ -27,15 +27,17 @@ export function renderTaskRemoteDelivery(delivery, indent = "") {
|
|
|
27
27
|
const lines = [
|
|
28
28
|
`${indent}Remote delivery: ${delivery.status}`,
|
|
29
29
|
`${indent}Expected heads: ${source}`,
|
|
30
|
-
`${indent}
|
|
31
|
-
`${indent}
|
|
32
|
-
`${indent}
|
|
30
|
+
`${indent}Required code Projects all merged: ${delivery.allMerged ? "yes" : "no"}`,
|
|
31
|
+
`${indent}Required code Projects all verified: ${delivery.allVerified ? "yes" : "no"}`,
|
|
32
|
+
`${indent}Required code Projects: ${delivery.mergedProjectCount}/${delivery.requiredCodeProjectCount} merged; ${delivery.verifiedProjectCount}/${delivery.requiredCodeProjectCount} verified; excluded=${delivery.excludedProjectCount}`,
|
|
33
|
+
...(delivery.requiredCodeProjectCount === 0 ? [`${indent}No delivery obligation.`] : []),
|
|
33
34
|
`${indent}Archive --integrated coverage: ${delivery.integratedCoverageSatisfied ? "satisfied" : "blocked"}`,
|
|
34
35
|
...(delivery.archiveDisposition === null
|
|
35
36
|
? []
|
|
36
37
|
: [`${indent}Archive disposition: ${delivery.archiveDisposition}`]),
|
|
37
38
|
`${indent}Projects:${delivery.projects.length === 0 ? " none" : ""}`,
|
|
38
39
|
...delivery.projects.map((project) => (`${indent}- ${project.directory} (${project.projectId}): `
|
|
40
|
+
+ `requirement=${project.deliveryRequirement}; scopeDecision=${project.scopeDecision?.id ?? "none"}; `
|
|
39
41
|
+ `expected=${shortCommit(project.expectedLocalCommit)}; `
|
|
40
42
|
+ `candidate=${shortCommit(project.deliveryLocalCommit)}; `
|
|
41
43
|
+ `adoption=${project.adoption?.id ?? "none"}; `
|
|
@@ -300,9 +300,9 @@ export function contextSnapshotDeltaRefIds(store, snapshot) {
|
|
|
300
300
|
throw new Error(`Context Snapshot parent is missing or drifted: ${snapshot.parentRef.id}.`);
|
|
301
301
|
}
|
|
302
302
|
validateContextSnapshot(parent);
|
|
303
|
-
const previous = new Map(parent.refs.map((ref) => [ref.refId
|
|
303
|
+
const previous = new Map(parent.refs.map((ref) => [`${ref.store}:${ref.refId}`, ref]));
|
|
304
304
|
const changed = snapshot.refs.filter((ref) => {
|
|
305
|
-
const before = previous.get(ref.refId);
|
|
305
|
+
const before = previous.get(`${ref.store}:${ref.refId}`);
|
|
306
306
|
return before === undefined
|
|
307
307
|
|| before.digest !== ref.digest
|
|
308
308
|
|| before.revision !== ref.revision
|
|
@@ -385,6 +385,7 @@ export function runContextEntry(pack) {
|
|
|
385
385
|
activeRuns: liveTaskState.activeRuns.length, activeTaskReviews: liveTaskState.activeTaskReviews.length }
|
|
386
386
|
},
|
|
387
387
|
reads: {
|
|
388
|
+
inbox: "yui inbox list --view current --json",
|
|
388
389
|
refs: `yui task run context ${pack.identity.taskId}/${pack.identity.runId} --view refs`,
|
|
389
390
|
full: `yui task run context ${pack.identity.taskId}/${pack.identity.runId} --view full`,
|
|
390
391
|
input: `yui task run context ${pack.identity.taskId}/${pack.identity.runId} --view input`,
|
|
@@ -440,7 +441,7 @@ export function expandRunContextRef(store, taskId, runId, refId, refStore) {
|
|
|
440
441
|
export function buildRunContextDelta(store, taskId, runId, after) {
|
|
441
442
|
const pack = buildRunContextPack(store, taskId, runId);
|
|
442
443
|
if (after === pack.digest || after === pack.snapshot.digest) {
|
|
443
|
-
return Object.freeze({ schemaVersion: 1, after, cursor: pack.digest, refs: [] });
|
|
444
|
+
return Object.freeze({ schemaVersion: 1, after, cursor: pack.digest, refs: [], removedRefs: [], complete: true });
|
|
444
445
|
}
|
|
445
446
|
const run = requireExactRun(store, taskId, runId);
|
|
446
447
|
const snapshot = readRunContextSnapshot(store, run);
|
|
@@ -449,11 +450,31 @@ export function buildRunContextDelta(store, taskId, runId, after) {
|
|
|
449
450
|
|| (parentDigest === undefined && after === "none"))) {
|
|
450
451
|
throw new Error("AgentRun Context delta cursor is outside the frozen Snapshot lineage.");
|
|
451
452
|
}
|
|
453
|
+
const parent = snapshot.parentRef === undefined ? null
|
|
454
|
+
: store.getContextSnapshot(taskId, snapshot.parentRef.id);
|
|
455
|
+
if (snapshot.parentRef !== undefined && (parent === null || parent.digest !== parentDigest
|
|
456
|
+
|| parent.sequence !== snapshot.parentRef.sequence)) {
|
|
457
|
+
throw new Error("AgentRun Context parent changed or is unavailable; reload the exact current pack.");
|
|
458
|
+
}
|
|
459
|
+
if (parent !== null)
|
|
460
|
+
validateContextSnapshot(parent);
|
|
461
|
+
const identity = (ref) => `${ref.store}:${ref.refId}`;
|
|
462
|
+
const previous = new Map((parent?.refs ?? []).map(ref => [identity(ref), ref]));
|
|
463
|
+
const present = new Set(pack.pointers.map(identity));
|
|
452
464
|
return Object.freeze({
|
|
453
465
|
schemaVersion: 1,
|
|
454
466
|
after,
|
|
455
467
|
cursor: pack.digest,
|
|
456
|
-
|
|
468
|
+
// deltaRefs is only a bounded launch hint. The immutable inventory (<=256)
|
|
469
|
+
// is the authority for a complete patch, including removals and changes
|
|
470
|
+
// beyond that hint's limit. Transport paginates large documents as usual.
|
|
471
|
+
refs: pack.pointers.filter(ref => {
|
|
472
|
+
const before = previous.get(identity(ref));
|
|
473
|
+
return before === undefined || contextContentDigest(before) !== contextContentDigest(ref);
|
|
474
|
+
}),
|
|
475
|
+
removedRefs: (parent?.refs ?? []).filter(ref => !present.has(identity(ref)))
|
|
476
|
+
.map(({ store, refId }) => ({ store, refId })),
|
|
477
|
+
complete: true
|
|
457
478
|
});
|
|
458
479
|
}
|
|
459
480
|
function requireExactRun(store, taskId, runId) {
|
|
@@ -90,6 +90,7 @@ export function serializeRunInputEnvelope(value) {
|
|
|
90
90
|
]),
|
|
91
91
|
"Use the Session Manifest's CLI entry. Fail closed if the exact Context is unavailable or mismatched.",
|
|
92
92
|
"Then read the relevant referenced requirements and perform the assigned work. Context loading is not the deliverable.",
|
|
93
|
+
"Current continuation/steer inputs: yui inbox list --view current --json. Read and claim exact originals, then ack their disposition before the final report; this does not accept the assignment.",
|
|
93
94
|
"During planning, fulfill the user's Draft discussion request without starting delivery before it is requested."
|
|
94
95
|
];
|
|
95
96
|
const serialized = lines.join("\n");
|
|
@@ -2,7 +2,7 @@ import { runExecutionObservation } from "../agentRun/agentRun.js";
|
|
|
2
2
|
import { isGitArtifactRefString, parseGitArtifactRef } from "../artifacts/gitArtifactRef.js";
|
|
3
3
|
import { taskNotFound, usageError } from "../errors/cliError.js";
|
|
4
4
|
import { expandTaskMessageResult } from "../message/message.js";
|
|
5
|
-
import { resolveManagedTaskReader } from "../runtime/managedCaller.js";
|
|
5
|
+
import { currentManagedRuntime, resolveManagedTaskReader } from "../runtime/managedCaller.js";
|
|
6
6
|
import { hasManagedIdentity } from "../runtime/managedIdentity.js";
|
|
7
7
|
import { providerRetrySummary } from "../runtime/providerRetry.js";
|
|
8
8
|
import { projectTaskRemoteDeliveryFromStore } from "../task/remoteDeliveryService.js";
|
|
@@ -12,6 +12,7 @@ import { contextContentDigest } from "./contextSnapshot.js";
|
|
|
12
12
|
import { buildRunContextPack } from "./runContextPack.js";
|
|
13
13
|
import { sourceRunContextValue } from "./sourceRunContext.js";
|
|
14
14
|
import { boundedDocument, fitsReadBudget, summaryText, recordPage } from "../output/boundedRead.js";
|
|
15
|
+
import { taskBriefGuard, taskPhase } from "./taskPhase.js";
|
|
15
16
|
const MAX_RECORDS = 64;
|
|
16
17
|
const MAX_VALUE_BYTES = 2048;
|
|
17
18
|
const MAX_PAGE_BYTES = 24 * 1024;
|
|
@@ -26,6 +27,9 @@ export function readTaskContext(store, taskId, environment = {}) {
|
|
|
26
27
|
const sources = authorizedSources(reader, scope, true);
|
|
27
28
|
const count = sources.reduce((total, source) => total + source.count, 0);
|
|
28
29
|
const cursor = currentCursor(reader, taskId);
|
|
30
|
+
const phase = scope.allow === undefined ? taskPhase(reader, scope.task) : undefined;
|
|
31
|
+
const phaseSource = phase?.activation?.source;
|
|
32
|
+
const priorMessageId = phaseSource?.priorMessageId;
|
|
29
33
|
// Reserve a small, classified view before ordinary history consumes the
|
|
30
34
|
// page. Counts and samples come from the exact same authorized read.
|
|
31
35
|
const attention = summarizeAttention(reader, taskId, scope.allow);
|
|
@@ -44,7 +48,20 @@ export function readTaskContext(store, taskId, environment = {}) {
|
|
|
44
48
|
const content = valueBytes > MAX_VALUE_BYTES || ["role", "task-message", "run", "work-item", "project-knowledge"].includes(entry.ref.store)
|
|
45
49
|
? { ref: entry.ref, summary: summarize(entry.value), omitted: true }
|
|
46
50
|
: { ...entry, omitted: false };
|
|
47
|
-
const
|
|
51
|
+
const revisedAfterActivation = entry.ref.store === "task-message" && phaseSource?.ref !== undefined
|
|
52
|
+
&& reader.queryContextRecords(taskId, {
|
|
53
|
+
family: "task-event", eventTypes: ["message.updated"], eventMessageId: entry.ref.refId,
|
|
54
|
+
afterSequence: sequence(phaseSource.ref.refId), limit: 0
|
|
55
|
+
}).count > 0;
|
|
56
|
+
const record = { ...content,
|
|
57
|
+
...(entry.ref.store === "task-message" && phase?.activation?.disposition === "adopted"
|
|
58
|
+
&& priorMessageId !== undefined ? {
|
|
59
|
+
phaseRelation: revisedAfterActivation ? "revised-after-activation-request"
|
|
60
|
+
: entry.ref.refId === phaseSource?.messageId ? "activation-source"
|
|
61
|
+
: Number(entry.ref.refId.slice("message-".length)) <= Number(priorMessageId.slice("message-".length))
|
|
62
|
+
? "recorded-before-activation-request" : "recorded-after-activation-request"
|
|
63
|
+
} : {}),
|
|
64
|
+
...(entry.ref.store === "run" ? {
|
|
48
65
|
execution: runExecutionObservation(entry.value, reader.getTaskRoleSessionSet(taskId, entry.value.roleName)?.providerBinding, runtimeEvents)
|
|
49
66
|
} : {}), ...(entry.ref.store === "role" ? {
|
|
50
67
|
providerRetry: providerRetrySummary(reader.getTaskRoleSessionSet(taskId, entry.value.name)?.providerBinding)
|
|
@@ -58,6 +75,14 @@ export function readTaskContext(store, taskId, environment = {}) {
|
|
|
58
75
|
const result = {
|
|
59
76
|
taskId, coreCursor: encode(cursor), throughCursor: encode(cursor),
|
|
60
77
|
view: "current",
|
|
78
|
+
...(scope.allow === undefined ? {
|
|
79
|
+
phase,
|
|
80
|
+
briefWriteGuard: taskBriefGuard(reader, scope.task)
|
|
81
|
+
} : {}),
|
|
82
|
+
caller: scope.caller === undefined ? { kind: "unmanaged-or-global" } : {
|
|
83
|
+
role: scope.caller.roleName,
|
|
84
|
+
executionAuthority: currentManagedRuntime(reader, environment, taskId)?.executionAuthority ?? "historical-read-only"
|
|
85
|
+
},
|
|
61
86
|
collections: Object.entries(sources.reduce((counts, source) => {
|
|
62
87
|
counts[source.family] = (counts[source.family] ?? 0) + source.count;
|
|
63
88
|
return counts;
|
|
@@ -68,7 +93,7 @@ export function readTaskContext(store, taskId, environment = {}) {
|
|
|
68
93
|
commandDiscovery: {
|
|
69
94
|
help: "yui help", describe: "yui --json help",
|
|
70
95
|
skillCheck: "yui session skills --json",
|
|
71
|
-
ownInbox: "yui inbox list --json",
|
|
96
|
+
ownInbox: "yui inbox list --view current --json",
|
|
72
97
|
note: "Help and completion share the current caller's offline command view. Follow that view for operation-specific discovery; execution always revalidates authority."
|
|
73
98
|
},
|
|
74
99
|
detail: `task context inspect ${taskId} --store <store> --ref <refId> --digest <digest>`,
|
|
@@ -133,11 +158,16 @@ export function readTaskContextDelta(store, taskId, input, environment = {}) {
|
|
|
133
158
|
const start = decode(input.after, taskId);
|
|
134
159
|
const now = currentCursor(reader, taskId);
|
|
135
160
|
const continuation = input.continuation === undefined ? undefined : decodePage(input.continuation, taskId);
|
|
161
|
+
if (continuation !== undefined && continuation.baseline !== encode(start)) {
|
|
162
|
+
throw usageError("Context continuation belongs to a different baseline; restart from the original starting cursor.");
|
|
163
|
+
}
|
|
136
164
|
const bound = continuation ?? now;
|
|
137
165
|
const after = continuation?.after ?? start.sequence;
|
|
138
|
-
if (start.
|
|
166
|
+
if (start.source !== now.source || bound.source !== now.source
|
|
167
|
+
|| start.sequence > bound.sequence || after < start.sequence || after > bound.sequence
|
|
168
|
+
|| start.revision > bound.revision
|
|
139
169
|
|| bound.sequence > now.sequence || start.revision > now.revision || bound.revision > now.revision) {
|
|
140
|
-
throw usageError(
|
|
170
|
+
throw usageError(`Context cursor is outside the current Task history; reread task context ${taskId} for a new bounded baseline.`);
|
|
141
171
|
}
|
|
142
172
|
const limit = input.limit ?? MAX_EVENTS;
|
|
143
173
|
if (!Number.isSafeInteger(limit) || limit < 1 || limit > MAX_EVENTS) {
|
|
@@ -163,11 +193,14 @@ export function readTaskContextDelta(store, taskId, input, environment = {}) {
|
|
|
163
193
|
}
|
|
164
194
|
const last = page.at(-1)?.ref.refId;
|
|
165
195
|
const more = selected.count > page.length;
|
|
166
|
-
const through = { taskId, sequence: bound.sequence, revision: bound.revision };
|
|
196
|
+
const through = { taskId, source: bound.source, sequence: bound.sequence, revision: bound.revision };
|
|
167
197
|
return {
|
|
168
|
-
taskId,
|
|
198
|
+
taskId, kind: "event-history", fromCursor: encode(start),
|
|
199
|
+
events: page, count: selected.count, complete: !more, throughCursor: encode(through),
|
|
200
|
+
stateChanged: start.revision !== bound.revision,
|
|
201
|
+
currentRead: `task context ${taskId}`,
|
|
169
202
|
...(more && last !== undefined
|
|
170
|
-
? { continuation: encode({ ...through, after: sequence(last) }) } : {}),
|
|
203
|
+
? { continuation: encode({ ...through, after: sequence(last), baseline: encode(start) }) } : {}),
|
|
171
204
|
observations: []
|
|
172
205
|
};
|
|
173
206
|
});
|
|
@@ -568,7 +601,8 @@ export function materialize(store, refId, value) {
|
|
|
568
601
|
return { ref: { store, refId, revision: String(record.revision ?? record.updatedAt ?? record.createdAt ?? digest), digest }, value };
|
|
569
602
|
}
|
|
570
603
|
function currentCursor(store, taskId) {
|
|
571
|
-
return { taskId,
|
|
604
|
+
return { taskId, source: store.getHomeIdentity().homeId,
|
|
605
|
+
sequence: store.latestEventSequence(taskId), revision: store.getStateRevision() };
|
|
572
606
|
}
|
|
573
607
|
function sequence(id) {
|
|
574
608
|
const result = /^event-(\d+)$/.exec(id);
|
|
@@ -584,20 +618,23 @@ function decode(value, taskId) {
|
|
|
584
618
|
if (value.length > 1024)
|
|
585
619
|
throw new Error("size");
|
|
586
620
|
const cursor = JSON.parse(Buffer.from(value, "base64url").toString());
|
|
587
|
-
if (cursor.taskId !== taskId ||
|
|
621
|
+
if (cursor.taskId !== taskId || typeof cursor.source !== "string"
|
|
622
|
+
|| !Number.isSafeInteger(cursor.sequence) || cursor.sequence < 0
|
|
588
623
|
|| !Number.isSafeInteger(cursor.revision) || cursor.revision < 0)
|
|
589
624
|
throw new Error("shape");
|
|
590
|
-
return cursor;
|
|
625
|
+
return { taskId: cursor.taskId, source: cursor.source, sequence: cursor.sequence, revision: cursor.revision };
|
|
591
626
|
}
|
|
592
627
|
catch {
|
|
593
|
-
throw usageError("Invalid Context cursor for this Task.");
|
|
628
|
+
throw usageError("Invalid or expired Context cursor for this Task; reread current Context for a new bounded baseline.");
|
|
594
629
|
}
|
|
595
630
|
}
|
|
596
631
|
function decodePage(value, taskId) {
|
|
597
|
-
const
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
632
|
+
const base = decode(value, taskId);
|
|
633
|
+
const cursor = JSON.parse(Buffer.from(value, "base64url").toString());
|
|
634
|
+
if (!Number.isSafeInteger(cursor.after) || cursor.after < 0 || typeof cursor.baseline !== "string") {
|
|
635
|
+
throw usageError("Invalid or expired Context continuation; reread current Context and start a new bounded window.");
|
|
636
|
+
}
|
|
637
|
+
return { ...base, after: cursor.after, baseline: cursor.baseline };
|
|
601
638
|
}
|
|
602
639
|
function freeze(value) {
|
|
603
640
|
if (value !== null && typeof value === "object") {
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { contextContentDigest } from "./contextSnapshot.js";
|
|
2
|
+
/** Existing immutable event payloads carry provenance, not a second authority
|
|
3
|
+
* record. No body or historical aggregate is copied into the current view. */
|
|
4
|
+
export function activationSourceEvent(store, task) {
|
|
5
|
+
const requestId = task.activationRequest?.operation.requestId;
|
|
6
|
+
if (requestId === undefined)
|
|
7
|
+
return undefined;
|
|
8
|
+
return store.queryContextRecords(task.id, {
|
|
9
|
+
family: "task-event", eventTypes: ["task.activation-requested"], eventRequestId: requestId,
|
|
10
|
+
descending: true, limit: 1
|
|
11
|
+
}).records[0]?.value;
|
|
12
|
+
}
|
|
13
|
+
export function taskPhase(store, task) {
|
|
14
|
+
const request = task.activationRequest;
|
|
15
|
+
const event = activationSourceEvent(store, task);
|
|
16
|
+
const messageId = event?.payload.sourceMessageId;
|
|
17
|
+
const message = messageId === undefined ? undefined : store.queryContextRecords(task.id, {
|
|
18
|
+
family: "task-message", ids: [messageId], limit: 1
|
|
19
|
+
}).records[0];
|
|
20
|
+
const body = message?.value?.body;
|
|
21
|
+
const status = event?.payload.sourceText !== undefined ? "available"
|
|
22
|
+
: messageId === undefined ? "missing"
|
|
23
|
+
: body === undefined ? "unavailable"
|
|
24
|
+
: contextContentDigest(body) === event?.payload.sourceBodyDigest ? "available" : "changed";
|
|
25
|
+
return {
|
|
26
|
+
status: task.status,
|
|
27
|
+
executionGate: task.executionGate.state,
|
|
28
|
+
brief: taskBriefFreshness(store, task),
|
|
29
|
+
activation: request === undefined ? null : {
|
|
30
|
+
requestId: request.operation.requestId, disposition: request.disposition,
|
|
31
|
+
actor: request.operation.actorId, effect: request.operation.effect,
|
|
32
|
+
...(request.afterPlanningRun === undefined ? {} : { afterPlanningRun: request.afterPlanningRun }),
|
|
33
|
+
source: {
|
|
34
|
+
status,
|
|
35
|
+
...(event === undefined ? {} : {
|
|
36
|
+
ref: { store: "task-event", refId: event.id, digest: contextContentDigest(event) },
|
|
37
|
+
read: `task event show ${task.id} ${event.id}`,
|
|
38
|
+
attribution: event.payload.sourceKind ?? "unspecified",
|
|
39
|
+
...(event.payload.priorMessageId === undefined ? {} : { priorMessageId: event.payload.priorMessageId }),
|
|
40
|
+
...(messageId === undefined ? {} : {
|
|
41
|
+
messageId, messageRead: `task message show ${task.id}/${messageId}`
|
|
42
|
+
})
|
|
43
|
+
}),
|
|
44
|
+
...(status === "available" ? {} : {
|
|
45
|
+
recovery: "Read the exact activation event and original input; missing provenance is not a new prohibition or permission."
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
export function activationWakeRefs(store, task) {
|
|
52
|
+
const event = activationSourceEvent(store, task);
|
|
53
|
+
if (event === undefined)
|
|
54
|
+
return [];
|
|
55
|
+
return [
|
|
56
|
+
{ type: "event", taskId: task.id, id: event.id },
|
|
57
|
+
...(event.payload.sourceMessageId === undefined ? [] : [
|
|
58
|
+
{ type: "message", taskId: task.id, id: event.payload.sourceMessageId }
|
|
59
|
+
])
|
|
60
|
+
];
|
|
61
|
+
}
|
|
62
|
+
const BRIEF_RELEVANT_EVENTS = [
|
|
63
|
+
"message.sent", "message.updated", "message.native-user-input", "brief.updated",
|
|
64
|
+
"decision.recorded", "decision.superseded", "task.project-delivery-decided", "input.requested", "input.answered", "input.cancelled", "input.auto-answered",
|
|
65
|
+
"run.completed", "run.failed", "run.cancelled", "job.state-changed", "work.accepted",
|
|
66
|
+
"runtime.role-delivery-failed", "run.retired",
|
|
67
|
+
"work.updated", "work.edited", "work.scope-updated", "work.rejected", "work.retired",
|
|
68
|
+
"review.completed", "review.failed", "integration.updated", "integration.completed", "integration.failed",
|
|
69
|
+
"milestone.added",
|
|
70
|
+
"task.activation-requested", "task.activation-adopted", "task.activated",
|
|
71
|
+
"task.execution-started", "task.execution-stopped", "task.reopened", "task.updated"
|
|
72
|
+
];
|
|
73
|
+
function latestBriefChange(store, taskId) {
|
|
74
|
+
return store.queryContextRecords(taskId, {
|
|
75
|
+
family: "task-event", eventTypes: BRIEF_RELEVANT_EVENTS, descending: true, limit: 1
|
|
76
|
+
}).records[0]?.value;
|
|
77
|
+
}
|
|
78
|
+
function taskBriefFreshness(store, task) {
|
|
79
|
+
const brief = store.getTaskBrief(task.id);
|
|
80
|
+
const last = latestBriefChange(store, task.id);
|
|
81
|
+
return {
|
|
82
|
+
status: brief === null ? "missing" : last === undefined ? "unverified"
|
|
83
|
+
: last.type === "brief.updated" ? "current-at-last-semantic-write" : "refresh-needed",
|
|
84
|
+
...(last === undefined ? {} : { changedBy: { store: "task-event", refId: last.id, type: last.type } }),
|
|
85
|
+
read: `task brief show ${task.id}`,
|
|
86
|
+
note: "Leader-maintained summary; lifecycle, gate and Session authority are separate live facts."
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/** Optimistic guard for semantic edits, independent of wall clocks and noisy
|
|
90
|
+
* runtime heartbeats. New input/result/decision/progress invalidates an old
|
|
91
|
+
* writer even when its Brief text itself has not changed. */
|
|
92
|
+
export function taskBriefGuard(store, task) {
|
|
93
|
+
const latest = latestBriefChange(store, task.id)?.id;
|
|
94
|
+
return contextContentDigest({
|
|
95
|
+
taskId: task.id, status: task.status, gate: task.executionGate,
|
|
96
|
+
title: task.title, description: task.description, activation: task.activationRequest,
|
|
97
|
+
brief: store.getTaskBrief(task.id), latest
|
|
98
|
+
});
|
|
99
|
+
}
|
package/dist/controlPlaneCli.js
CHANGED
|
@@ -39,6 +39,7 @@ import { runConfigCommand } from "./commands/configCommands.js";
|
|
|
39
39
|
import { runConfigOverview } from "./commands/configOverview.js";
|
|
40
40
|
import { parseControllerCleanupOptions, parseControllerRuntimeSnapshot, parseControllerStatusOptions, renderControllerResourceStatus, renderRuntimeIdentitySection, runInteractiveControllerCleanup, summarizeDurablePhysicalMismatch } from "./commands/controllerCommands.js";
|
|
41
41
|
import { runDurableJobCommand } from "./commands/durableJobCommands.js";
|
|
42
|
+
import { runScheduleCommand } from "./commands/scheduleCommands.js";
|
|
42
43
|
import { parseExecutionAuditOptions, runExecutionAuditCommand } from "./commands/executionAuditCommands.js";
|
|
43
44
|
import { runGlobalRoleCommand, previewGlobalRoleAgentConfigurationMutation } from "./commands/globalRoleCommands.js";
|
|
44
45
|
import { runJobCommand } from "./commands/jobCommands.js";
|
|
@@ -167,8 +168,6 @@ export async function main() {
|
|
|
167
168
|
const discovery = discoveryCommandTree(process.env);
|
|
168
169
|
invocationHome = home;
|
|
169
170
|
const routedForFence = args.length === 0 ? undefined : routeInvocation(args);
|
|
170
|
-
const managedInvocation = process.env.YUI_SESSION_SCOPE === "task"
|
|
171
|
-
|| process.env.YUI_SESSION_SCOPE === "global";
|
|
172
171
|
const homeFreeInvocation = args.length === 0
|
|
173
172
|
|| (args[0] === "config" && (args[1] === "describe"
|
|
174
173
|
|| (args[1] === "completion" && args[2] === "candidates")))
|
|
@@ -678,29 +677,46 @@ export async function main() {
|
|
|
678
677
|
});
|
|
679
678
|
const workspaceCoordinator = new TaskWorkspaceCoordinator(store, workspacePreparer, runtime);
|
|
680
679
|
if (resolved[0] === "web") {
|
|
681
|
-
if (
|
|
682
|
-
|
|
683
|
-
|
|
680
|
+
if (hasManagedIdentity(process.env)) {
|
|
681
|
+
const current = requireManagedCaller(store, process.env);
|
|
682
|
+
if (current.scope !== "global" || current.roleName !== "operator") {
|
|
683
|
+
throw usageError("Only a local user or the current global Operator may manage Web listeners.");
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
const caller = resolveJobCaller(process.env, "");
|
|
687
|
+
const describe = (status) => status
|
|
688
|
+
? `Web listener: ${status.state}\n`
|
|
689
|
+
+ (status.alreadyRunning ? "Existing listener reused; credentials were not reloaded.\n" : "")
|
|
690
|
+
+ (status.authentication === "basic" ? "Browser authentication: username yui; use the password from your private credential file.\n" : "")
|
|
691
|
+
+ (status.urls ?? []).map(url => `Yui web control room: ${url}\n`).join("")
|
|
692
|
+
+ (status.warnings ?? []).map(warning => `Warning: ${warning}\n`).join("")
|
|
693
|
+
: "Web is not running. Start it explicitly; Controller recovery does not restore listeners.\n";
|
|
684
694
|
if (resolved.length === 2 && (resolved[1] === "--status" || resolved[1] === "--stop")) {
|
|
685
|
-
const status = await callController(home, "web.status", {});
|
|
695
|
+
const status = await callController(home, "web.status", { caller });
|
|
686
696
|
if (resolved[1] === "--status")
|
|
687
|
-
emit(status
|
|
697
|
+
emit(describe(status), false, status);
|
|
688
698
|
else {
|
|
689
|
-
if (status)
|
|
690
|
-
|
|
691
|
-
|
|
699
|
+
if (status && !status.id)
|
|
700
|
+
throw usageError("Web listener is starting; query status before stopping.");
|
|
701
|
+
const result = status ? await callController(home, "web.stop", { id: status.id, caller }) : { stopped: false };
|
|
702
|
+
emit("Web listener stopped or already absent; Controller and Agents are unchanged.\n", false, result);
|
|
692
703
|
}
|
|
693
704
|
return;
|
|
694
705
|
}
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
const options = parseWebCommandOptions(resolved.slice(1));
|
|
706
|
+
const detached = jsonOutput || caller.scope === "global" || resolved.includes("--detach");
|
|
707
|
+
const options = parseWebCommandOptions(resolved.slice(1).filter(arg => arg !== "--detach"));
|
|
698
708
|
const id = randomUUID();
|
|
699
|
-
await callController(home, "web.start", { ...options, id });
|
|
700
|
-
|
|
701
|
-
|
|
709
|
+
const status = await callController(home, "web.start", { ...options, id, caller });
|
|
710
|
+
emit(describe(status), false, status);
|
|
711
|
+
if (detached || status.alreadyRunning)
|
|
712
|
+
return;
|
|
702
713
|
await new Promise((resolve) => {
|
|
714
|
+
// The Controller owns the listening socket, so this CLI otherwise has
|
|
715
|
+
// no referenced handle: a Promise/signal handler alone does not keep
|
|
716
|
+
// Node alive for the foreground user's Ctrl-C.
|
|
717
|
+
const keepAlive = setInterval(() => { }, 60_000);
|
|
703
718
|
const stop = () => {
|
|
719
|
+
clearInterval(keepAlive);
|
|
704
720
|
process.off("SIGINT", stop);
|
|
705
721
|
process.off("SIGTERM", stop);
|
|
706
722
|
resolve();
|
|
@@ -708,7 +724,7 @@ export async function main() {
|
|
|
708
724
|
process.once("SIGINT", stop);
|
|
709
725
|
process.once("SIGTERM", stop);
|
|
710
726
|
});
|
|
711
|
-
await callController(home, "web.stop", { id });
|
|
727
|
+
await callController(home, "web.stop", { id, caller });
|
|
712
728
|
return;
|
|
713
729
|
}
|
|
714
730
|
if (resolved[0] === "config") {
|
|
@@ -1963,6 +1979,11 @@ export async function main() {
|
|
|
1963
1979
|
emit(runJobCommand(resolved.slice(1), store, { runtime }));
|
|
1964
1980
|
return;
|
|
1965
1981
|
}
|
|
1982
|
+
if (resolved[0] === "schedule") {
|
|
1983
|
+
const result = await runScheduleCommand(resolved.slice(1), { home, environment: process.env });
|
|
1984
|
+
emit(JSON.stringify(result, null, 2), false, result);
|
|
1985
|
+
return;
|
|
1986
|
+
}
|
|
1966
1987
|
if (resolved[0] === "job") {
|
|
1967
1988
|
emit(await runDurableJobCommand(resolved.slice(1), {
|
|
1968
1989
|
home,
|