@zq-silk/yui 0.16.1 → 1.0.0-alpha
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/ARCHITECTURE.md +12 -0
- package/ARCHITECTURE.zh-CN.md +8 -0
- package/README.md +9 -4
- package/dist/agent/agent.js +4 -9
- package/dist/agent/executionComponents.js +4 -4
- package/dist/agent/managedRuntimeEnvironment.js +0 -4
- package/dist/agentRun/agentRun.js +13 -23
- package/dist/artifacts/managedGit.js +3 -56
- package/dist/brief/taskBrief.js +3 -3
- package/dist/cli/agentConfigurationPicker.js +24 -21
- package/dist/cli/commandCatalog.js +18 -10
- package/dist/cli/roleWizard.js +41 -30
- package/dist/cli/updateCommand.js +35 -13
- package/dist/cli/updateOrchestrator.js +73 -44
- package/dist/cli/updatePorts.js +76 -86
- package/dist/cli/upgradeCommand.js +4 -8
- package/dist/cli.js +39 -33
- package/dist/commands/controllerCommands.js +2 -2
- package/dist/commands/executionAuditCommands.js +2 -2
- package/dist/commands/projectCommands.js +1 -1
- package/dist/commands/releaseCommands.js +9 -38
- package/dist/commands/taskCommands.js +91 -81
- package/dist/commands/taskCompletionGate.js +0 -117
- package/dist/commands/taskRoleRuntimeStatus.js +2 -2
- package/dist/commands/taskUpstreamCommands.js +93 -54
- package/dist/context/runContextPack.js +30 -54
- package/dist/context/runInputContract.js +9 -0
- package/dist/context/sessionBootstrapManifest.js +11 -19
- package/dist/controller/agentHostObservation.js +4 -2
- package/dist/controller/agentRuntimeObserver.js +4 -4
- package/dist/controller/clientRuntime.js +19 -44
- package/dist/controller/controller.js +24 -10
- package/dist/controller/fileSchedulerStoreAdapter.js +91 -24
- package/dist/controller/globalInputDelivery.js +31 -8
- package/dist/controller/globalRuntimeAttention.js +34 -0
- package/dist/controller/jobSupervisor.js +3 -3
- package/dist/controller/operatorNotification.js +31 -0
- package/dist/controller/providerRetryAdmission.js +3 -1
- package/dist/controller/providerRetryDelivery.js +136 -119
- package/dist/controller/runtime.js +2 -28
- package/dist/controller/sessionOwnerReconciliation.js +42 -24
- package/dist/controller/structuredProviderObservation.js +28 -1
- package/dist/controller/updateReconciliation.js +72 -20
- package/dist/coordination/workMailbox.js +4 -4
- package/dist/core/controllerIdentity.js +25 -0
- package/dist/core/controllerProcessIdentity.js +2 -2
- package/dist/core/controllerServer.js +4 -2
- package/dist/core/protocol.js +1 -1
- package/dist/doctor/doctor.js +2 -1
- package/dist/domain/validation.js +6 -0
- package/dist/event/taskEvent.js +3 -3
- package/dist/execution/workItemExecution.js +2 -2
- package/dist/executor/agentAdapter.js +28 -96
- package/dist/executor/agentConfigurationCatalog.js +13 -68
- package/dist/executor/agentConfigurationFields.js +120 -0
- package/dist/executor/agentConfigurationProbe.js +55 -80
- package/dist/executor/agentExecutor.js +11 -8
- package/dist/executor/effectiveLaunch.js +3 -3
- package/dist/executor/executorRegistry.js +0 -9
- package/dist/executor/fileRoleLaunchPlanner.js +12 -11
- package/dist/grant/capabilityGrant.js +2 -2
- package/dist/input/inputRequest.js +4 -4
- package/dist/integration/changeSet.js +3 -3
- package/dist/integration/integrationAttempt.js +3 -3
- package/dist/integration/integrationSourceApplication.js +1 -1
- package/dist/interaction/operatorPresentation.js +2 -1
- package/dist/job/durableJob.js +1 -1
- package/dist/job/jobRunner.js +2 -2
- package/dist/message/message.js +8 -6
- package/dist/message/messageContinuation.js +8 -9
- package/dist/milestone/milestone.js +3 -3
- package/dist/observability/executionAudit.js +1 -10
- package/dist/observability/runtimeIdentity.js +0 -23
- package/dist/output/agentConfigurationPresentation.js +4 -2
- package/dist/profile/agentProfile.js +3 -3
- package/dist/release/releaseHandover.js +2 -2
- package/dist/release/releaseIdempotencyStore.js +0 -23
- package/dist/release/releaseWorkflowPorts.js +8 -4
- package/dist/release/runtimeRelease.js +9 -7
- package/dist/repository/gitWorkspace.js +66 -19
- package/dist/repository/project.js +3 -3
- package/dist/repository/taskWorkspacePreparer.js +75 -68
- package/dist/resources/liveReferences.js +1 -1
- package/dist/resources/resourceRegistry.js +90 -44
- package/dist/resources/sqliteResourceRegistry.js +5 -7
- package/dist/review/reviewRound.js +5 -5
- package/dist/role/role.js +7 -8
- package/dist/runtime/acpProtocol.js +2 -3
- package/dist/runtime/acpSession.js +21 -5
- package/dist/runtime/agentDriverObservation.js +1 -1
- package/dist/runtime/agentEndpoint.js +4 -1
- package/dist/runtime/agentHost.js +25 -65
- package/dist/runtime/agentHostCleanup.js +85 -0
- package/dist/runtime/agentHostProtocol.js +3 -3
- package/dist/runtime/builtinAgentDrivers.js +46 -119
- package/dist/runtime/builtinTranscriptObserver.js +10 -6
- package/dist/runtime/codexAppServerRuntime.js +55 -89
- package/dist/runtime/codexInteractiveHost.js +1 -1
- package/dist/runtime/jsonLineChannel.js +35 -7
- package/dist/runtime/launchBroker.js +1 -1
- package/dist/runtime/processExitObservation.js +1 -1
- package/dist/runtime/providerContinuationReconciliationService.js +86 -38
- package/dist/runtime/providerRetry.js +15 -0
- package/dist/runtime/providerRuntimeIdentity.js +3 -3
- package/dist/runtime/providerRuntimeReconciler.js +12 -2
- package/dist/runtime/runtimeCoherence.js +7 -3
- package/dist/runtime/runtimeObservation.js +3 -3
- package/dist/runtime/sessionOwnerIdentity.js +1 -1
- package/dist/runtime/structuredProviderHost.js +106 -81
- package/dist/runtime/taskRuntimeIsolation.js +3 -3
- package/dist/runtime/tmuxAdapters.js +3 -3
- package/dist/scheduler/activeRoleRunDelivery.js +5 -1
- package/dist/scheduler/operatorInputNotificationProcessor.js +7 -17
- package/dist/scheduler/taskWake.js +1 -1
- package/dist/storage/baselineSchema.js +606 -0
- package/dist/storage/homeLayout.js +5 -16
- package/dist/storage/recordValidation.js +16 -4
- package/dist/storage/sqliteSchema.js +106 -1741
- package/dist/storage/sqliteStore.js +16 -14
- package/dist/storage/storageSchema.js +8 -7
- package/dist/storage/storageVersions.js +23 -16
- package/dist/storage/taskStore.js +3 -29
- package/dist/storage/upgrade/upgradeOrchestrator.js +14 -112
- package/dist/task/nextAction.js +33 -23
- package/dist/task/task.js +17 -8
- package/dist/task/taskActivation.js +5 -4
- package/dist/telemetry/sqliteTelemetryStore.js +2 -2
- package/dist/verification/gateArtifact.js +5 -3
- package/dist/verification/verificationPlan.js +6 -7
- package/dist/web/assets/assetManifest.js +2 -0
- package/dist/web/assets/client/app.js +72 -3
- package/dist/web/assets/client/components.js +2 -1
- package/dist/web/assets/client/i18n.js +2 -0
- package/dist/web/assets/client/taskSummary.js +345 -0
- package/dist/web/assets/client/taskSurface.js +44 -26
- package/dist/web/assets/client/view.js +33 -0
- package/dist/web/assets/shell.js +1 -0
- package/dist/web/assets/styles/cards.js +21 -0
- package/dist/web/webServer.js +39 -5
- package/dist/web/webSessions.js +165 -0
- package/dist/web/webSnapshot.js +23 -0
- package/dist/web/webTaskSurface.js +23 -0
- package/dist/workItem/workItem.js +6 -6
- package/dist/workspace/cleanupInspection.js +1 -9
- package/dist/worktree/managedWorkspace.js +3 -3
- package/docs/managed-turn-and-session-runtime.md +50 -7
- package/docs/managed-turn-and-session-runtime.zh-CN.md +36 -5
- package/docs/observability/README.md +47 -0
- package/docs/observability/README.zh-CN.md +37 -0
- package/docs/project-refresh.md +9 -0
- package/docs/project-refresh.zh-CN.md +8 -0
- package/docs/provider-retry.md +35 -0
- package/docs/release-workflow.md +70 -214
- package/docs/release-workflow.zh-CN.md +53 -157
- package/docs/roles-and-configuration.md +30 -0
- package/docs/roles-and-configuration.zh-CN.md +20 -0
- package/docs/sqlite-control-plane-design.md +48 -289
- package/docs/sqlite-control-plane-design.zh-CN.md +37 -53
- package/docs/storage-baseline.md +132 -0
- package/docs/storage-baseline.zh-CN.md +106 -0
- package/docs/task-delivery.md +110 -2
- package/docs/task-delivery.zh-CN.md +82 -2
- package/docs/task-discovery.md +9 -0
- package/docs/task-discovery.zh-CN.md +6 -0
- package/docs/testing/verification-levels.md +42 -164
- package/docs/testing/verification-levels.zh-CN.md +29 -115
- package/i18n/README.zh-CN.md +7 -4
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +11 -0
- package/skills/yui-leader/references/execution.md +22 -0
- package/skills/yui-leader/references/planning.md +6 -2
- package/skills/yui-operator/SKILL.md +45 -13
- package/skills/yui-operator/references/task-delivery.md +99 -0
- package/skills/yui-runtime/SKILL.md +5 -3
- package/skills/yui-runtime/references/publication.md +56 -5
- package/skills/yui-runtime/references/recovery.md +12 -2
- package/dist/runtime/agentHostCompatibility.js +0 -127
- package/dist/storage/migrations/agentFailureContext.js +0 -22
- package/dist/storage/migrations/agentRunContract.js +0 -159
- package/dist/storage/migrations/artifactsToGit.js +0 -338
- package/dist/storage/migrations/collapseWorktreeLayout.js +0 -963
- package/dist/storage/migrations/currentInputContract.js +0 -86
- package/dist/storage/migrations/currentRuntimeContract.js +0 -228
- package/dist/storage/migrations/historicalVerificationPlan.js +0 -35
- package/dist/storage/migrations/integrationContinuation.js +0 -105
- package/dist/storage/migrations/narrowAgentFailureContext.js +0 -65
- package/dist/storage/migrations/notificationOnlyWakes.js +0 -74
- package/dist/storage/migrations/removeRuntimeGeneration.js +0 -207
- package/dist/storage/migrations/submitIntent.js +0 -126
- package/dist/storage/migrations/unifyHomeLayout.js +0 -925
- package/dist/storage/migrations/verificationPlanV1.js +0 -162
- package/dist/storage/migrations/verificationPolicy.js +0 -74
- package/dist/storage/migrations/workItemHistory.js +0 -46
package/dist/cli/roleWizard.js
CHANGED
|
@@ -2,7 +2,7 @@ import { renderTable } from "../output/table.js";
|
|
|
2
2
|
import { displayExecutionComponent } from "../agent/executionComponents.js";
|
|
3
3
|
import { isAgentAdapterId } from "../agent/adapterCatalog.js";
|
|
4
4
|
import { defaultRoleAgentConfig } from "../executor/agentAdapter.js";
|
|
5
|
-
import { selectAgentEffort, selectAgentModelAndEffort } from "./agentConfigurationPicker.js";
|
|
5
|
+
import { selectAgentEffort, selectAgentModelAndEffort, renderAgentConfigurationResolutionNotice } from "./agentConfigurationPicker.js";
|
|
6
6
|
const TEXT_COLUMN = { header: "Selection", minWidth: 12, maxWidth: 34 };
|
|
7
7
|
export async function resolveRoleWizardArguments(commandArgs, ports, io) {
|
|
8
8
|
const args = [...commandArgs];
|
|
@@ -231,6 +231,8 @@ async function configureNewAgentField(args, agent, ports, io) {
|
|
|
231
231
|
args: selection.effort === undefined ? args : [...args, "--effort", selection.effort]
|
|
232
232
|
};
|
|
233
233
|
}
|
|
234
|
+
if (resolved !== undefined)
|
|
235
|
+
io.write(renderAgentConfigurationResolutionNotice(resolved));
|
|
234
236
|
if (selected.value === "permission-strategy") {
|
|
235
237
|
const strategy = await promptAgentFieldValue(selected, io);
|
|
236
238
|
if (strategy === undefined || strategy.length === 0)
|
|
@@ -407,6 +409,8 @@ async function updateAgentBindingSettings(args, role, binding, ports, io) {
|
|
|
407
409
|
]
|
|
408
410
|
};
|
|
409
411
|
}
|
|
412
|
+
if (resolved !== undefined)
|
|
413
|
+
io.write(renderAgentConfigurationResolutionNotice(resolved));
|
|
410
414
|
if (selectedField.value === "permission-strategy") {
|
|
411
415
|
const strategy = await promptAgentFieldValue(selectedField, io);
|
|
412
416
|
if (strategy === undefined || strategy.length === 0)
|
|
@@ -446,23 +450,17 @@ function agentFields(binding, catalog) {
|
|
|
446
450
|
return [
|
|
447
451
|
agentField("model", "Model", config.model, "--model", ["--clear-model"]),
|
|
448
452
|
agentField("effort", "Effort", config.effort, "--effort", ["--clear-effort"]),
|
|
449
|
-
agentField("permission-strategy", "Permission strategy", permission.strategy, "--permission-strategy", ["--permission-strategy", "default"],
|
|
453
|
+
agentField("permission-strategy", "Permission strategy", permission.strategy, "--permission-strategy", ["--permission-strategy", "default"], catalogField(catalog, "permission.strategy")),
|
|
450
454
|
...(binding.adapterId === "codex" && permission.strategy === "configured" ? [
|
|
451
|
-
agentField("sandbox", "Sandbox", permission.sandbox, "--sandbox", ["--permission-strategy", "default"],
|
|
452
|
-
|
|
453
|
-
])),
|
|
454
|
-
agentField("approval", "Approval", permission.approval, "--approval", ["--permission-strategy", "default"], catalogChoices(catalog, "permission.approval", [
|
|
455
|
-
"untrusted", "on-request", "never"
|
|
456
|
-
]))
|
|
455
|
+
agentField("sandbox", "Sandbox", permission.sandbox, "--sandbox", ["--permission-strategy", "default"], catalogField(catalog, "permission.sandbox")),
|
|
456
|
+
agentField("approval", "Approval", permission.approval, "--approval", ["--permission-strategy", "default"], catalogField(catalog, "permission.approval"))
|
|
457
457
|
] : []),
|
|
458
458
|
...(binding.adapterId === "codex" ? [
|
|
459
|
-
agentField("search", "Web search", config.search, "--search", ["--clear-search"],
|
|
460
|
-
...catalogChoices(catalog, "search", ["true"])
|
|
461
|
-
])
|
|
459
|
+
agentField("search", "Web search", config.search, "--search", ["--clear-search"], catalogField(catalog, "search"))
|
|
462
460
|
] : []),
|
|
463
461
|
...((binding.adapterId === "claude" || binding.adapterId === "acp")
|
|
464
462
|
&& permission.strategy === "configured" ? [
|
|
465
|
-
agentField("permission-mode", "Permission mode", permission.mode, "--permission-mode", ["--permission-strategy", "default"],
|
|
463
|
+
agentField("permission-mode", "Permission mode", permission.mode, "--permission-mode", ["--permission-strategy", "default"], catalogField(catalog, "permission.mode"))
|
|
466
464
|
] : [])
|
|
467
465
|
];
|
|
468
466
|
}
|
|
@@ -479,9 +477,8 @@ async function loadAgentCatalog(ports, binding, cwd) {
|
|
|
479
477
|
? value
|
|
480
478
|
: undefined;
|
|
481
479
|
}
|
|
482
|
-
function
|
|
483
|
-
|
|
484
|
-
return field === undefined ? [...fallback] : field.choices.map(({ value }) => value);
|
|
480
|
+
function catalogField(catalog, key) {
|
|
481
|
+
return catalog?.fields.find((candidate) => candidate.key === key);
|
|
485
482
|
}
|
|
486
483
|
function appendModelEffortPatch(args, selection, update) {
|
|
487
484
|
return [
|
|
@@ -494,8 +491,19 @@ function appendModelEffortPatch(args, selection, update) {
|
|
|
494
491
|
: ["--effort", selection.effort])
|
|
495
492
|
];
|
|
496
493
|
}
|
|
497
|
-
function agentField(value, label, current, set, clear,
|
|
498
|
-
|
|
494
|
+
function agentField(value, label, current, set, clear, capability) {
|
|
495
|
+
const reported = capability?.available === false ? [] : capability?.choices.map(choice => choice.value) ?? [];
|
|
496
|
+
const retained = (typeof current === "string" || typeof current === "boolean")
|
|
497
|
+
&& !reported.includes(String(current)) ? [String(current)] : [];
|
|
498
|
+
return {
|
|
499
|
+
value, label, current: display(current), set, clear,
|
|
500
|
+
choices: [...reported, ...retained], allowCustom: capability?.allowCustom === true,
|
|
501
|
+
reason: [
|
|
502
|
+
capability?.reason ?? (capability === undefined
|
|
503
|
+
? "No catalog field was reported; no choices inferred." : ""),
|
|
504
|
+
...(retained.length === 0 ? [] : ["Current value is retained but not reported by this catalog."])
|
|
505
|
+
].filter(Boolean).join(" ")
|
|
506
|
+
};
|
|
499
507
|
}
|
|
500
508
|
async function configuredPermissionArgs(binding, catalog, strategy, io) {
|
|
501
509
|
if (strategy !== "configured")
|
|
@@ -509,12 +517,8 @@ async function configuredPermissionArgs(binding, catalog, strategy, io) {
|
|
|
509
517
|
return undefined;
|
|
510
518
|
const option = field === "sandbox" ? "--sandbox" : "--approval";
|
|
511
519
|
const value = await promptAgentFieldValue(agentField(field, field === "sandbox" ? "Sandbox" : "Approval", undefined, option, ["--permission-strategy", "default"], field === "sandbox"
|
|
512
|
-
?
|
|
513
|
-
|
|
514
|
-
])
|
|
515
|
-
: catalogChoices(catalog, "permission.approval", [
|
|
516
|
-
"untrusted", "on-request", "never"
|
|
517
|
-
])), io);
|
|
520
|
+
? catalogField(catalog, "permission.sandbox")
|
|
521
|
+
: catalogField(catalog, "permission.approval")), io);
|
|
518
522
|
return value === undefined
|
|
519
523
|
? undefined
|
|
520
524
|
: ["--permission-strategy", "configured", option, value];
|
|
@@ -523,9 +527,9 @@ async function configuredPermissionArgs(binding, catalog, strategy, io) {
|
|
|
523
527
|
// An ACP Session's configured permission is one mode the Agent enumerated,
|
|
524
528
|
// so there is nothing to choose between: ask for the mode directly rather
|
|
525
529
|
// than offering tool rules the protocol has no place for. The choices come
|
|
526
|
-
// from the
|
|
527
|
-
//
|
|
528
|
-
const value = await promptAgentFieldValue(agentField("permission-mode", "Permission mode", undefined, "--permission-mode", ["--permission-strategy", "default"],
|
|
530
|
+
// from the catalog. An explicitly open field permits a custom mode before
|
|
531
|
+
// Session creation; it is checked against the native Session at launch.
|
|
532
|
+
const value = await promptAgentFieldValue(agentField("permission-mode", "Permission mode", undefined, "--permission-mode", ["--permission-strategy", "default"], catalogField(catalog, "permission.mode")), io);
|
|
529
533
|
return value === undefined || value.length === 0
|
|
530
534
|
? undefined
|
|
531
535
|
: ["--permission-strategy", "configured", "--permission-mode", value];
|
|
@@ -541,16 +545,23 @@ async function configuredPermissionArgs(binding, catalog, strategy, io) {
|
|
|
541
545
|
? "--permission-mode"
|
|
542
546
|
: field === "allowed" ? "--allowed-tool" : "--disallowed-tool";
|
|
543
547
|
const value = field === "mode"
|
|
544
|
-
? await promptAgentFieldValue(agentField("permission-mode", "Permission mode", undefined, option, ["--permission-strategy", "default"],
|
|
548
|
+
? await promptAgentFieldValue(agentField("permission-mode", "Permission mode", undefined, option, ["--permission-strategy", "default"], catalogField(catalog, "permission.mode")), io)
|
|
545
549
|
: (await io.question(`${field === "allowed" ? "Allowed" : "Disallowed"} tool: `))?.trim();
|
|
546
550
|
return value === undefined || value.length === 0
|
|
547
551
|
? undefined
|
|
548
552
|
: ["--permission-strategy", "configured", option, value];
|
|
549
553
|
}
|
|
550
554
|
async function promptAgentFieldValue(field, io) {
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
555
|
+
if (field.reason)
|
|
556
|
+
io.write(`${field.label}: ${field.reason}\n`);
|
|
557
|
+
if (field.choices === undefined)
|
|
558
|
+
return (await io.question(`${field.label}: `))?.trim();
|
|
559
|
+
const custom = "\0yui:custom";
|
|
560
|
+
const selected = await choose(`Set ${field.label}`, [
|
|
561
|
+
...field.choices.map(choice => ({ value: choice, cells: [choice] })),
|
|
562
|
+
...(field.allowCustom ? [{ value: custom, cells: ["Custom value (native acceptance unverified)"] }] : [])
|
|
563
|
+
], [TEXT_COLUMN], io, field.choices.includes(field.current) ? field.current : field.choices[0], "value");
|
|
564
|
+
return selected === custom ? (await io.question(`Custom ${field.label}: `))?.trim() : selected;
|
|
554
565
|
}
|
|
555
566
|
async function selectActiveAgent(role, ports, io) {
|
|
556
567
|
const configured = await configuredAgents(ports);
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { resolveYuiHome } from "../storage/taskStore.js";
|
|
2
2
|
import { createUpdatePorts } from "./updatePorts.js";
|
|
3
3
|
import { runUpdate } from "./updateOrchestrator.js";
|
|
4
|
+
import { isConcreteVersion } from "../domain/validation.js";
|
|
5
|
+
import { usageError } from "../errors/cliError.js";
|
|
4
6
|
/**
|
|
5
7
|
* Run `yui update` as a side-by-side, recoverable orchestration.
|
|
6
8
|
*
|
|
@@ -11,15 +13,24 @@ import { runUpdate } from "./updateOrchestrator.js";
|
|
|
11
13
|
*
|
|
12
14
|
* Returns a process exit code: 0 on success or already-current, 5 on abort.
|
|
13
15
|
*/
|
|
14
|
-
export function runUpdateCommand(environment = process.env, spawn, write = (text) => process.stdout.write(text), ports) {
|
|
16
|
+
export function runUpdateCommand(args = [], environment = process.env, spawn, write = (text) => process.stdout.write(text), ports) {
|
|
17
|
+
const version = parseUpdateVersion(args);
|
|
15
18
|
const home = resolveYuiHome(environment);
|
|
16
19
|
const resolvedPorts = ports ?? createUpdatePorts(environment, spawn);
|
|
17
|
-
const result = runUpdate(resolvedPorts, { home });
|
|
20
|
+
const result = runUpdate(resolvedPorts, { home, version });
|
|
18
21
|
write(`${renderUpdateResult(result)}\n`);
|
|
19
22
|
if (result.outcome === "aborted")
|
|
20
23
|
return 5;
|
|
21
24
|
return 0;
|
|
22
25
|
}
|
|
26
|
+
function parseUpdateVersion(args) {
|
|
27
|
+
if (args.length === 0)
|
|
28
|
+
return undefined;
|
|
29
|
+
if (args.length === 2 && args[0] === "--version" && isConcreteVersion(args[1])) {
|
|
30
|
+
return args[1].trim();
|
|
31
|
+
}
|
|
32
|
+
throw usageError("Update usage: yui update [--version <exact-version>]");
|
|
33
|
+
}
|
|
23
34
|
/** Render an {@link UpdateResult} as concise, CLI-style text. */
|
|
24
35
|
export function renderUpdateResult(result) {
|
|
25
36
|
const rendered = (() => {
|
|
@@ -33,14 +44,18 @@ export function renderUpdateResult(result) {
|
|
|
33
44
|
case "aborted":
|
|
34
45
|
return [
|
|
35
46
|
`Update aborted during ${result.phase}: ${result.message}`,
|
|
36
|
-
result.
|
|
37
|
-
||
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
47
|
+
result.controllerOwnershipUnknown === true || result.controllerRestore?.outcome === "unknown"
|
|
48
|
+
|| result.controllerReconciliation?.attempts.some(attempt => attempt.outcome === "unknown")
|
|
49
|
+
|| result.controllerReconciliation?.observationError !== undefined
|
|
50
|
+
? "Controller resource effects are uncertain; do not assume the Home is quiesced or usable."
|
|
51
|
+
: result.phase === "migrate-storage"
|
|
52
|
+
|| (result.phase === "post-verify" && result.recoverable)
|
|
53
|
+
? "The target binary is installed; the Home remains quiesced pending successful verification."
|
|
54
|
+
: result.recoverable
|
|
55
|
+
? "The current install and Home remain usable."
|
|
56
|
+
: result.phase === "activate-binary"
|
|
57
|
+
? "The Home was unchanged, but binary health is unknown; do not assume the current install is usable."
|
|
58
|
+
: "Manual recovery is required (see below).",
|
|
44
59
|
...(result.backupPath === undefined
|
|
45
60
|
? []
|
|
46
61
|
: [`Storage backup: ${result.backupPath}`]),
|
|
@@ -48,7 +63,14 @@ export function renderUpdateResult(result) {
|
|
|
48
63
|
].join("\n");
|
|
49
64
|
}
|
|
50
65
|
})();
|
|
51
|
-
return
|
|
52
|
-
|
|
53
|
-
|
|
66
|
+
return [
|
|
67
|
+
rendered,
|
|
68
|
+
...(result.controllerReconciliation === undefined ? [] : [
|
|
69
|
+
`Controller reconciliation: ${JSON.stringify(result.controllerReconciliation)}`
|
|
70
|
+
]),
|
|
71
|
+
...(result.controllerRestore === undefined ? [] : [
|
|
72
|
+
`Controller restore: ${JSON.stringify(result.controllerRestore)}`
|
|
73
|
+
]),
|
|
74
|
+
...(result.cleanupWarning === undefined ? [] : [`Warning: ${result.cleanupWarning}`])
|
|
75
|
+
].join("\n");
|
|
54
76
|
}
|
|
@@ -6,10 +6,12 @@
|
|
|
6
6
|
* then activated, any required storage migration is applied, and the result is
|
|
7
7
|
* verified before the Controller is restarted.
|
|
8
8
|
*/
|
|
9
|
+
import { UpdateControllerReconciliationError } from "../controller/updateReconciliation.js";
|
|
10
|
+
import { isControllerIdentity } from "../core/controllerIdentity.js";
|
|
9
11
|
export function runUpdate(ports, options) {
|
|
10
12
|
let staged;
|
|
11
13
|
try {
|
|
12
|
-
staged = ports.stage();
|
|
14
|
+
staged = ports.stage(options.version);
|
|
13
15
|
}
|
|
14
16
|
catch (error) {
|
|
15
17
|
return {
|
|
@@ -33,7 +35,9 @@ export function runUpdate(ports, options) {
|
|
|
33
35
|
cleanupWarning = `Staging cleanup could not be completed: ${messageOf(error)}`;
|
|
34
36
|
}
|
|
35
37
|
}
|
|
36
|
-
return cleanupWarning === undefined ? result : {
|
|
38
|
+
return cleanupWarning === undefined ? result : {
|
|
39
|
+
...result, cleanupWarning: [result.cleanupWarning, cleanupWarning].filter(Boolean).join(" ")
|
|
40
|
+
};
|
|
37
41
|
}
|
|
38
42
|
function runStagedUpdate(ports, staged, home) {
|
|
39
43
|
let preflight;
|
|
@@ -58,8 +62,6 @@ function runStagedUpdate(ports, staged, home) {
|
|
|
58
62
|
action: preflight.action,
|
|
59
63
|
recoverable: true,
|
|
60
64
|
version: staged.version,
|
|
61
|
-
...(preflight.blockers === undefined ? {} : { blockers: preflight.blockers }),
|
|
62
|
-
...(preflight.retryCommand === undefined ? {} : { retryCommand: preflight.retryCommand }),
|
|
63
65
|
...(preflight.sceneUnchanged === true ? { sceneUnchanged: true } : {})
|
|
64
66
|
};
|
|
65
67
|
}
|
|
@@ -97,38 +99,69 @@ function runStagedUpdate(ports, staged, home) {
|
|
|
97
99
|
version: staged.version
|
|
98
100
|
};
|
|
99
101
|
}
|
|
102
|
+
let result;
|
|
103
|
+
let cleanupWarning;
|
|
100
104
|
try {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
// An older Controller can finish a launch between preflight and drain.
|
|
105
|
-
// Recheck after its exact stop, before changing the install or storage.
|
|
106
|
-
let fencedPreflight;
|
|
105
|
+
result = reconcileAndUpdate(ports, staged, home);
|
|
106
|
+
}
|
|
107
|
+
finally {
|
|
107
108
|
try {
|
|
108
|
-
|
|
109
|
+
releaseHandover?.();
|
|
109
110
|
}
|
|
110
111
|
catch (error) {
|
|
111
|
-
|
|
112
|
-
outcome: "aborted", phase: "preflight",
|
|
113
|
-
message: `Quiesced compatibility preflight failed: ${messageOf(error)}`,
|
|
114
|
-
action: "The install and storage are unchanged; inspect the compatibility failure before retrying.",
|
|
115
|
-
recoverable: true, version: staged.version
|
|
116
|
-
});
|
|
112
|
+
cleanupWarning = `Controller handover lock release failed for ${home}: ${messageOf(error)}`;
|
|
117
113
|
}
|
|
118
|
-
if (fencedPreflight.status === "blocked") {
|
|
119
|
-
return restoreControllerOrReport(ports, home, captured.lifecycle, {
|
|
120
|
-
outcome: "aborted", phase: "preflight",
|
|
121
|
-
message: fencedPreflight.message, action: fencedPreflight.action,
|
|
122
|
-
recoverable: true, version: staged.version,
|
|
123
|
-
...(fencedPreflight.blockers === undefined ? {} : { blockers: fencedPreflight.blockers }),
|
|
124
|
-
...(fencedPreflight.sceneUnchanged === true ? { sceneUnchanged: true } : {})
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
return activateAndVerify(ports, staged, home, captured.lifecycle, fencedPreflight);
|
|
128
114
|
}
|
|
129
|
-
|
|
130
|
-
|
|
115
|
+
return cleanupWarning === undefined ? result : { ...result, cleanupWarning };
|
|
116
|
+
}
|
|
117
|
+
function reconcileAndUpdate(ports, staged, home) {
|
|
118
|
+
let reconciliation;
|
|
119
|
+
try {
|
|
120
|
+
reconciliation = ports.reconcileController?.(home);
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
const evidence = error instanceof UpdateControllerReconciliationError ? error.result : undefined;
|
|
124
|
+
const unknown = evidence === undefined || evidence.observationError !== undefined
|
|
125
|
+
|| evidence.lockReleaseError !== undefined || evidence.attempts.some(attempt => attempt.outcome === "unknown");
|
|
126
|
+
return {
|
|
127
|
+
outcome: "aborted", phase: "coordination", version: staged.version,
|
|
128
|
+
message: `Controller reconciliation failed: ${messageOf(error)}`,
|
|
129
|
+
action: "Inspect the reported current-Home targets and partial effects; binary activation was not attempted.",
|
|
130
|
+
recoverable: !unknown,
|
|
131
|
+
...(evidence === undefined ? {} : { controllerReconciliation: evidence }),
|
|
132
|
+
...(unknown ? { controllerOwnershipUnknown: true } : {})
|
|
133
|
+
};
|
|
131
134
|
}
|
|
135
|
+
const coordinated = runCoordinatedUpdate(ports, staged, home);
|
|
136
|
+
return reconciliation === undefined ? coordinated : { ...coordinated, controllerReconciliation: reconciliation };
|
|
137
|
+
}
|
|
138
|
+
function runCoordinatedUpdate(ports, staged, home) {
|
|
139
|
+
const captured = captureControllerLifecycle(ports, staged.version, home);
|
|
140
|
+
if ("outcome" in captured)
|
|
141
|
+
return captured;
|
|
142
|
+
// Storage can change before the Controller drains. Recheck after its
|
|
143
|
+
// exact stop, before changing the install or storage. Its sceneUnchanged
|
|
144
|
+
// verdict does not describe the preceding runtime cleanup/stop/restore.
|
|
145
|
+
let fencedPreflight;
|
|
146
|
+
try {
|
|
147
|
+
fencedPreflight = ports.preflight(staged, home);
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
return restoreControllerOrReport(ports, home, captured.lifecycle, {
|
|
151
|
+
outcome: "aborted", phase: "preflight",
|
|
152
|
+
message: `Quiesced storage preflight failed: ${messageOf(error)}`,
|
|
153
|
+
action: "The install and storage are unchanged; inspect the storage failure before retrying.",
|
|
154
|
+
recoverable: true, version: staged.version
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
if (fencedPreflight.status === "blocked") {
|
|
158
|
+
return restoreControllerOrReport(ports, home, captured.lifecycle, {
|
|
159
|
+
outcome: "aborted", phase: "preflight",
|
|
160
|
+
message: fencedPreflight.message, action: fencedPreflight.action,
|
|
161
|
+
recoverable: true, version: staged.version
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
return activateAndVerify(ports, staged, home, captured.lifecycle, fencedPreflight);
|
|
132
165
|
}
|
|
133
166
|
function activateAndVerify(ports, staged, home, lifecycle, preflight) {
|
|
134
167
|
try {
|
|
@@ -256,7 +289,8 @@ function captureControllerLifecycle(ports, version, home) {
|
|
|
256
289
|
phase: "preflight",
|
|
257
290
|
message: `Controller status could not be verified: ${messageOf(error)}`,
|
|
258
291
|
action: "Inspect Controller ownership and retry after its status is known.",
|
|
259
|
-
recoverable:
|
|
292
|
+
recoverable: false,
|
|
293
|
+
controllerOwnershipUnknown: true,
|
|
260
294
|
version
|
|
261
295
|
};
|
|
262
296
|
}
|
|
@@ -279,7 +313,8 @@ function captureControllerLifecycle(ports, version, home) {
|
|
|
279
313
|
phase: "preflight",
|
|
280
314
|
message: `Controller stop/drain failed: ${messageOf(error)}`,
|
|
281
315
|
action: "Inspect the captured Controller before retrying; binary activation was not attempted.",
|
|
282
|
-
recoverable:
|
|
316
|
+
recoverable: false,
|
|
317
|
+
controllerOwnershipUnknown: true,
|
|
283
318
|
version
|
|
284
319
|
};
|
|
285
320
|
}
|
|
@@ -300,7 +335,8 @@ function malformedControllerResult(version, message) {
|
|
|
300
335
|
phase: "preflight",
|
|
301
336
|
message,
|
|
302
337
|
action: "Refusing an unfenced Controller handoff; inspect ownership and retry.",
|
|
303
|
-
recoverable:
|
|
338
|
+
recoverable: false,
|
|
339
|
+
controllerOwnershipUnknown: true,
|
|
304
340
|
version
|
|
305
341
|
};
|
|
306
342
|
}
|
|
@@ -309,14 +345,16 @@ function restoreControllerOrReport(ports, home, lifecycle, failure) {
|
|
|
309
345
|
return failure;
|
|
310
346
|
try {
|
|
311
347
|
ports.restoreController(home, lifecycle.identity);
|
|
312
|
-
return failure;
|
|
348
|
+
return { ...failure, controllerRestore: { identity: lifecycle.identity, outcome: "restored" } };
|
|
313
349
|
}
|
|
314
350
|
catch (error) {
|
|
315
351
|
return {
|
|
316
352
|
...failure,
|
|
317
353
|
message: `${failure.message} Captured Controller restore failed: ${messageOf(error)}.`,
|
|
318
|
-
action: `${failure.action}
|
|
319
|
-
recoverable: false
|
|
354
|
+
action: `${failure.action} Controller liveness after the failed restore is unknown; inspect its exact identity before further action.`,
|
|
355
|
+
recoverable: false,
|
|
356
|
+
controllerOwnershipUnknown: true,
|
|
357
|
+
controllerRestore: { identity: lifecycle.identity, outcome: "unknown", error: messageOf(error) }
|
|
320
358
|
};
|
|
321
359
|
}
|
|
322
360
|
}
|
|
@@ -351,15 +389,6 @@ function isStorageMigrationResult(value) {
|
|
|
351
389
|
function isPositivePid(value) {
|
|
352
390
|
return typeof value === "number" && Number.isSafeInteger(value) && value > 0;
|
|
353
391
|
}
|
|
354
|
-
function isControllerIdentity(value) {
|
|
355
|
-
return isRecord(value)
|
|
356
|
-
&& typeof value.executablePath === "string"
|
|
357
|
-
&& value.executablePath.length > 0
|
|
358
|
-
&& Array.isArray(value.args)
|
|
359
|
-
&& value.args.every((arg) => typeof arg === "string")
|
|
360
|
-
&& typeof value.version === "string"
|
|
361
|
-
&& value.version.length > 0;
|
|
362
|
-
}
|
|
363
392
|
function isRecord(value) {
|
|
364
393
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
365
394
|
}
|