maka-agent 0.2.0-dev.22.20260905 → 0.2.0-dev.24.20260907
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/acp/maka-acp-agent.js +2 -1
- package/dist/acp/session-configuration.js +143 -0
- package/dist/acp/session-registry.js +90 -4
- package/dist/cli-core.js +2 -0
- package/dist/pi-transcript.js +5 -12
- package/dist/pi-tui-runner.js +74 -4
- package/dist/runtime-host-cli-context.js +167 -78
- package/dist/runtime-host-cli-installation.js +8 -2
- package/dist/runtime-host-cli.js +31 -1
- package/dist/runtime-host-handoff-surface.js +81 -0
- package/dist/runtime-host-installed-update-bootstrap.js +13 -0
- package/dist/runtime-host-installed-update-coordinator.js +33 -0
- package/dist/runtime-host-lifecycle-transaction.js +9 -1
- package/dist/runtime-host-local-handoff.js +37 -4
- package/dist/runtime-host-local-managed-activation.js +84 -0
- package/dist/runtime-host-local-source-retirement.js +18 -2
- package/dist/runtime-host-managed-lifecycle-manager.js +1 -0
- package/dist/runtime-host-operator-retirement.js +50 -0
- package/dist/runtime-host-service-manager.js +3 -0
- package/dist/runtime-host-session-driver.js +4 -25
- package/dist/runtime-host-session-update.js +62 -0
- package/dist/runtime-host-tui-command.js +38 -73
- package/dist/runtime-host-tui-context.js +1 -0
- package/dist/runtime-host-tui-owner.js +226 -0
- package/dist/tui-copy-catalog.js +140 -3
- package/dist/tui-host-owner-copy.js +24 -0
- package/native/runtime-host-peer/prebuilds/darwin-arm64/maka_runtime_host_peer.node +0 -0
- package/native/runtime-host-peer/prebuilds/linux-arm64/maka_runtime_host_peer.node +0 -0
- package/native/runtime-host-peer/prebuilds/linux-x64/maka_runtime_host_peer.node +0 -0
- package/native/runtime-host-peer/prebuilds/win32-x64/maka_runtime_host_peer.node +0 -0
- package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
- package/node_modules/@maka/core/dist/bot-events.js +18 -63
- package/node_modules/@maka/core/dist/canonical-runtime-event.js +3 -1
- package/node_modules/@maka/core/dist/model-call-attempt.js +0 -82
- package/node_modules/@maka/core/dist/model-call-usage-projection.js +0 -186
- package/node_modules/@maka/core/dist/model-failure.js +40 -0
- package/node_modules/@maka/core/dist/model-metadata.generated.js +4 -4
- package/node_modules/@maka/core/dist/model-metadata.js +3 -3
- package/node_modules/@maka/core/dist/model-web-search.js +5 -4
- package/node_modules/@maka/core/dist/provider-registry.js +45 -4
- package/node_modules/@maka/core/dist/record-schema.js +22 -2
- package/node_modules/@maka/core/dist/redaction.js +41 -58
- package/node_modules/@maka/core/dist/runtime-boundary.js +31 -11
- package/node_modules/@maka/core/dist/runtime-event.js +37 -6
- package/node_modules/@maka/core/dist/runtime-handoff.js +98 -0
- package/node_modules/@maka/core/dist/runtime-invocation.js +1 -1
- package/node_modules/@maka/core/dist/runtime-logical-execution.js +112 -0
- package/node_modules/@maka/core/dist/session-trace.js +5 -1
- package/node_modules/@maka/core/dist/session.js +54 -52
- package/node_modules/@maka/core/dist/slash-command-catalog.js +2 -0
- package/node_modules/@maka/core/dist/tool-result-archive-evidence.js +20 -0
- package/node_modules/@maka/core/dist/usage-ledger-merge.js +7 -9
- package/node_modules/@maka/core/dist/usage-record-schema.js +26 -35
- package/node_modules/@maka/core/dist/workhub-creation-intent.js +34 -20
- package/node_modules/@maka/core/package.json +4 -0
- package/node_modules/@maka/eval/harbor/egress-proxy/entrypoint.sh +2 -1
- package/node_modules/@maka/mcp/dist/index.js +131 -1
- package/node_modules/@maka/runtime/dist/agent-run.js +272 -105
- package/node_modules/@maka/runtime/dist/ai-sdk-backend.js +13 -5
- package/node_modules/@maka/runtime/dist/ai-sdk-compaction.js +109 -34
- package/node_modules/@maka/runtime/dist/ai-sdk-turn.js +155 -134
- package/node_modules/@maka/runtime/dist/bots/base-adapter.js +17 -0
- package/node_modules/@maka/runtime/dist/bots/bot-test.js +38 -22
- package/node_modules/@maka/runtime/dist/bots/dingtalk-bridge.js +7 -5
- package/node_modules/@maka/runtime/dist/bots/discord-bridge.js +4 -4
- package/node_modules/@maka/runtime/dist/bots/feishu-bridge.js +4 -5
- package/node_modules/@maka/runtime/dist/bots/qq-bridge.js +6 -4
- package/node_modules/@maka/runtime/dist/bots/slack-bridge.js +3 -4
- package/node_modules/@maka/runtime/dist/bots/telegram-bridge.js +4 -5
- package/node_modules/@maka/runtime/dist/bots/wechat-bridge.js +17 -19
- package/node_modules/@maka/runtime/dist/bots/wecom-bridge.js +5 -6
- package/node_modules/@maka/runtime/dist/bots/ws-bridge-base.js +2 -3
- package/node_modules/@maka/runtime/dist/context-budget.js +14 -9
- package/node_modules/@maka/runtime/dist/conversation-copy.js +5 -4
- package/node_modules/@maka/runtime/dist/goal-continuation.js +36 -4
- package/node_modules/@maka/runtime/dist/history-compact-checkpoint.js +18 -9
- package/node_modules/@maka/runtime/dist/history-compact-summarizer.js +30 -22
- package/node_modules/@maka/runtime/dist/history-compaction.js +29 -4
- package/node_modules/@maka/runtime/dist/ledger-tool-result-archive-reader.js +106 -0
- package/node_modules/@maka/runtime/dist/message-authority.js +9 -0
- package/node_modules/@maka/runtime/dist/model-adapter.js +19 -98
- package/node_modules/@maka/runtime/dist/model-factory.js +19 -81
- package/node_modules/@maka/runtime/dist/model-fetcher.js +7 -0
- package/node_modules/@maka/runtime/dist/model-runtime.js +85 -111
- package/node_modules/@maka/runtime/dist/native-web-search-tool.js +2 -1
- package/node_modules/@maka/runtime/dist/openai-codex-history-compactor.js +2 -2
- package/node_modules/@maka/runtime/dist/opencode-session-header.js +4 -2
- package/node_modules/@maka/runtime/dist/provider-error-classification.js +84 -123
- package/node_modules/@maka/runtime/dist/provider-request-telemetry.js +68 -108
- package/node_modules/@maka/runtime/dist/run-handoff-gate.js +97 -0
- package/node_modules/@maka/runtime/dist/runtime-event-backfill.js +82 -54
- package/node_modules/@maka/runtime/dist/runtime-event-read-model.js +117 -159
- package/node_modules/@maka/runtime/dist/runtime-kernel.js +150 -112
- package/node_modules/@maka/runtime/dist/runtime-ledger-repair.js +160 -79
- package/node_modules/@maka/runtime/dist/runtime-read-model.js +30 -89
- package/node_modules/@maka/runtime/dist/runtime-resume.js +80 -4
- package/node_modules/@maka/runtime/dist/session-event-runtime-mapper.js +5 -0
- package/node_modules/@maka/runtime/dist/session-manager.js +171 -288
- package/node_modules/@maka/runtime/dist/session-projection-helpers.js +0 -27
- package/node_modules/@maka/runtime/dist/session-trace-projection.js +3 -30
- package/node_modules/@maka/runtime/dist/subscription-model-fetch.js +3 -4
- package/node_modules/@maka/runtime/dist/test-connection.js +16 -16
- package/node_modules/@maka/runtime/dist/tool-result-archive-encoding.js +38 -0
- package/node_modules/@maka/runtime/dist/tool-runtime.js +3 -61
- package/node_modules/@maka/runtime/package.json +1 -0
- package/node_modules/@maka/runtime-host/dist/adapter/session-projector.js +35 -0
- package/node_modules/@maka/runtime-host/dist/candidate-entry.js +4 -1
- package/node_modules/@maka/runtime-host/dist/client/connect-or-spawn.js +9 -1
- package/node_modules/@maka/runtime-host/dist/client/connection.js +3 -0
- package/node_modules/@maka/runtime-host/dist/client/host-handoff-copy.js +156 -0
- package/node_modules/@maka/runtime-host/dist/client/host-handoff.js +273 -0
- package/node_modules/@maka/runtime-host/dist/client/host-retirement.js +26 -5
- package/node_modules/@maka/runtime-host/dist/client/index.js +3 -1
- package/node_modules/@maka/runtime-host/dist/client/launcher.js +3 -0
- package/node_modules/@maka/runtime-host/dist/client/peer-client.js +13 -0
- package/node_modules/@maka/runtime-host/dist/client/registered-host-termination.js +0 -59
- package/node_modules/@maka/runtime-host/dist/client/remote-compatibility-error.js +2 -0
- package/node_modules/@maka/runtime-host/dist/client/session-subscription.js +27 -5
- package/node_modules/@maka/runtime-host/dist/operator/index.js +2 -1
- package/node_modules/@maka/runtime-host/dist/operator/retirement-cancellation.js +20 -0
- package/node_modules/@maka/runtime-host/dist/peer-mesh/node.js +28 -16
- package/node_modules/@maka/runtime-host/dist/protocol/external-session.js +4 -0
- package/node_modules/@maka/runtime-host/dist/protocol/host-status.js +29 -12
- package/node_modules/@maka/runtime-host/dist/protocol/index.js +37 -1
- package/node_modules/@maka/runtime-host/dist/protocol/operations.js +2 -0
- package/node_modules/@maka/runtime-host/dist/protocol/session-collaboration.js +28 -1
- package/node_modules/@maka/runtime-host/dist/protocol/session-continuity.js +40 -20
- package/node_modules/@maka/runtime-host/dist/protocol/session-transcript.js +0 -5
- package/node_modules/@maka/runtime-host/dist/protocol/session-turns.js +29 -58
- package/node_modules/@maka/runtime-host/dist/protocol/turn.js +11 -1
- package/node_modules/@maka/runtime-host/dist/protocol/workhub-coordination.js +33 -0
- package/node_modules/@maka/runtime-host/dist/server/access-authority.js +26 -1
- package/node_modules/@maka/runtime-host/dist/server/access-credential-store.js +6 -1
- package/node_modules/@maka/runtime-host/dist/server/canonical-turn-snapshot.js +19 -4
- package/node_modules/@maka/runtime-host/dist/server/canonical-usage-reader.js +24 -11
- package/node_modules/@maka/runtime-host/dist/server/connection-session.js +7 -6
- package/node_modules/@maka/runtime-host/dist/server/daily-review-coordinator.js +7 -4
- package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +168 -28
- package/node_modules/@maka/runtime-host/dist/server/execution-model-authority.js +5 -5
- package/node_modules/@maka/runtime-host/dist/server/execution-model-composition.js +3 -2
- package/node_modules/@maka/runtime-host/dist/server/external-session-coordinator.js +6 -3
- package/node_modules/@maka/runtime-host/dist/server/goal-coordinator.js +40 -1
- package/node_modules/@maka/runtime-host/dist/server/host-kernel.js +108 -23
- package/node_modules/@maka/runtime-host/dist/server/host-residency-registry.js +13 -7
- package/node_modules/@maka/runtime-host/dist/server/hosted-execution-admission-registry.js +3 -1
- package/node_modules/@maka/runtime-host/dist/server/hosted-execution-recovery.js +124 -62
- package/node_modules/@maka/runtime-host/dist/server/message-coordinator.js +85 -8
- package/node_modules/@maka/runtime-host/dist/server/root-admission-owner.js +7 -0
- package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +344 -71
- package/node_modules/@maka/runtime-host/dist/server/scheduled-task-coordinator.js +37 -2
- package/node_modules/@maka/runtime-host/dist/server/serial-outbound-writer.js +45 -3
- package/node_modules/@maka/runtime-host/dist/server/session-catalog-coordinator.js +201 -12
- package/node_modules/@maka/runtime-host/dist/server/session-continuity-coordinator.js +87 -25
- package/node_modules/@maka/runtime-host/dist/server/session-revision-coordinator.js +8 -55
- package/node_modules/@maka/runtime-host/dist/server/session-sidecar-purge.js +1 -16
- package/node_modules/@maka/runtime-host/dist/server/session-transcript-pager.js +0 -1
- package/node_modules/@maka/runtime-host/dist/server/session-transcript-reader.js +460 -70
- package/node_modules/@maka/runtime-host/dist/server/shared-session-transcript.js +1 -1
- package/node_modules/@maka/runtime-host/dist/server/storage-maintenance.js +109 -0
- package/node_modules/@maka/runtime-host/dist/server/usage-pricing-coordinator.js +4 -11
- package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-action-gate.js +44 -15
- package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-coordinator.js +6 -2
- package/node_modules/@maka/storage/dist/agent-run-store.js +44 -0
- package/node_modules/@maka/storage/dist/artifact-store.js +21 -10
- package/node_modules/@maka/storage/dist/artifact-stores.js +1 -1
- package/node_modules/@maka/storage/dist/claude-code-session-adapter.js +0 -4
- package/node_modules/@maka/storage/dist/codex-session-adapter.js +0 -2
- package/node_modules/@maka/storage/dist/context-offload-snapshot.js +325 -0
- package/node_modules/@maka/storage/dist/conversation-operational-state.js +4 -0
- package/node_modules/@maka/storage/dist/execution-stores.js +11 -7
- package/node_modules/@maka/storage/dist/file-session-repository.js +1012 -0
- package/node_modules/@maka/storage/dist/model-call-ledger.js +199 -32
- package/node_modules/@maka/storage/dist/model-call-usage-sql.js +136 -0
- package/node_modules/@maka/storage/dist/opencode-session-adapter.js +0 -4
- package/node_modules/@maka/storage/dist/operational-state-backup.js +9 -5
- package/node_modules/@maka/storage/dist/operational-state-store-public.js +1 -1
- package/node_modules/@maka/storage/dist/operational-state-store.js +5 -3
- package/node_modules/@maka/storage/dist/runtime-event-persistence.js +1 -0
- package/node_modules/@maka/storage/dist/runtime-transcript-query.js +308 -0
- package/node_modules/@maka/storage/dist/session-bundle-policy.js +24 -4
- package/node_modules/@maka/storage/dist/session-message-projection.js +15 -0
- package/node_modules/@maka/storage/dist/session-repository.js +767 -0
- package/node_modules/@maka/storage/dist/session-store.js +25 -99
- package/node_modules/@maka/storage/dist/sqlite-artifact-metadata.js +10 -3
- package/node_modules/@maka/storage/dist/sqlite-context-offload-store.js +24 -8
- package/node_modules/@maka/storage/dist/sqlite-core-execution-schema.js +17 -1
- package/node_modules/@maka/storage/dist/sqlite-runtime-schema.js +44 -1
- package/node_modules/@maka/storage/dist/sqlite-runtime-store.js +129 -25
- package/node_modules/@maka/storage/dist/sqlite-session-metadata-store.js +109 -892
- package/node_modules/@maka/storage/dist/sqlite-usage-schema.js +200 -20
- package/node_modules/@maka/storage/dist/tool-result-archive-evidence.js +111 -0
- package/node_modules/@maka/storage/dist/usage-stores.js +6 -2
- package/node_modules/@maka/storage/package.json +2 -0
- package/package.json +1 -1
|
@@ -16,13 +16,16 @@
|
|
|
16
16
|
* specific language governing permissions and limitations
|
|
17
17
|
* under the License.
|
|
18
18
|
*/
|
|
19
|
+
import { activateLocalManagedRuntimeHost } from './runtime-host-local-managed-activation.js';
|
|
19
20
|
import { randomUUID } from 'node:crypto';
|
|
20
21
|
import { join } from 'node:path';
|
|
21
22
|
import { NO_REAL_CONNECTION_CODE } from '@maka/core/connection-error-copy';
|
|
22
|
-
import { connectOrSpawnRuntimeHost, connectRuntimeHostProfile, createClientRuntimeHostProfileCatalog, createRuntimeHostPeerClientFromEnvironment, createRuntimeHostReconnectingConnection, loadOrCreateRuntimeHostClientInstanceId, LOCAL_RUNTIME_HOST_PROFILE, readRuntimeHostConnectionCatalog, RuntimeHostPermanentReconnectError, runtimeHostStartupError, } from '@maka/runtime-host/client';
|
|
23
|
-
import { INTERACTIVE_RUNTIME_HOST_COMPOSITION_ID,
|
|
24
|
-
import { readLocalHostDeploymentRecord } from '@maka/runtime-host/operator';
|
|
23
|
+
import { connectOrSpawnRuntimeHost, connectRuntimeHost, connectRuntimeHostProfile, createClientRuntimeHostProfileCatalog, createRuntimeHostPeerClientFromEnvironment, createRuntimeHostReconnectingConnection, loadOrCreateRuntimeHostClientInstanceId, LOCAL_RUNTIME_HOST_PROFILE, readRuntimeHostConnectionCatalog, RuntimeHostPermanentReconnectError, RuntimeHostRemoteCompatibilityError, runHostHandoff, runtimeHostStartupError, } from '@maka/runtime-host/client';
|
|
24
|
+
import { INTERACTIVE_RUNTIME_HOST_COMPOSITION_ID, RUNTIME_HOST_PROTOCOL_VERSION, } from '@maka/runtime-host/protocol';
|
|
25
|
+
import { readLocalHostDeploymentRecord, resolveRuntimeHostManagedDeploymentAuthority, } from '@maka/runtime-host/operator';
|
|
25
26
|
import { resolveMakaClientDataRoot } from '@maka/storage/workspace-root';
|
|
27
|
+
import { isTemporaryNpxInstallation, resolveRuntimeHostNpmGlobalInstallation, } from './runtime-host-cli-installation.js';
|
|
28
|
+
import { restartRuntimeHostNpmGlobalDeployment, runtimeHostNpmGlobalSourceRetirementAvailable, } from './runtime-host-local-handoff.js';
|
|
26
29
|
/**
|
|
27
30
|
* The mode a new Session starts in belongs to the Host: `session.create`
|
|
28
31
|
* falls back to `chatDefaults.permissionMode` in the Runtime Policy whenever a
|
|
@@ -42,17 +45,6 @@ import { resolveMakaClientDataRoot } from '@maka/storage/workspace-root';
|
|
|
42
45
|
export async function readHostChatDefaultPermissionMode(connection) {
|
|
43
46
|
return (await connection.request('runtime.policy.query', {})).policy.chatDefaults.permissionMode;
|
|
44
47
|
}
|
|
45
|
-
export class RuntimeHostCliConflictError extends RuntimeHostPermanentReconnectError {
|
|
46
|
-
handshake;
|
|
47
|
-
registration;
|
|
48
|
-
code = 'RUNTIME_HOST_RESTART_REQUIRED';
|
|
49
|
-
constructor(handshake, registration) {
|
|
50
|
-
super(formatRuntimeHostCliConflict(handshake, registration));
|
|
51
|
-
this.handshake = handshake;
|
|
52
|
-
this.registration = registration;
|
|
53
|
-
this.name = 'RuntimeHostCliConflictError';
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
48
|
export async function connectRuntimeHostCli(input, overrides = {}) {
|
|
57
49
|
const context = await connectRuntimeHostCliConnection(input, overrides);
|
|
58
50
|
try {
|
|
@@ -67,12 +59,19 @@ export async function connectRuntimeHostCli(input, overrides = {}) {
|
|
|
67
59
|
export async function connectRuntimeHostCliConnection(input, overrides = {}) {
|
|
68
60
|
const deps = {
|
|
69
61
|
connectOrSpawn: connectOrSpawnRuntimeHost,
|
|
62
|
+
activateLocalManagedHost: activateLocalManagedRuntimeHost,
|
|
63
|
+
connectActivatedHost: connectRuntimeHost,
|
|
70
64
|
connectProfile: connectRuntimeHostProfile,
|
|
71
65
|
readConnectionCatalog: readRuntimeHostConnectionCatalog,
|
|
72
66
|
loadClientInstanceId: loadOrCreateRuntimeHostClientInstanceId,
|
|
73
67
|
executionCandidateEntrypoint: new URL(import.meta.resolve('@maka/runtime-host/execution-candidate-main')),
|
|
74
68
|
readDeploymentRecord: readLocalHostDeploymentRecord,
|
|
69
|
+
resolveManagedAuthority: resolveRuntimeHostManagedDeploymentAuthority,
|
|
75
70
|
createPeerClient: createRuntimeHostPeerClientFromEnvironment,
|
|
71
|
+
resolveInstallation: resolveRuntimeHostNpmGlobalInstallation,
|
|
72
|
+
isTemporaryNpxInstallation,
|
|
73
|
+
restartDeployment: restartRuntimeHostNpmGlobalDeployment,
|
|
74
|
+
sourceRetirementAvailable: runtimeHostNpmGlobalSourceRetirementAvailable,
|
|
76
75
|
...overrides,
|
|
77
76
|
};
|
|
78
77
|
const resolvedProfile = await resolveHostProfile(input, deps);
|
|
@@ -83,45 +82,171 @@ export async function connectRuntimeHostCliConnection(input, overrides = {}) {
|
|
|
83
82
|
const peerClient = profile.kind === 'remote' && profile.transport.kind === 'libp2p-direct'
|
|
84
83
|
? deps.createPeerClient()
|
|
85
84
|
: undefined;
|
|
85
|
+
// A temporary package is evidence for invocation lifetime, never deployment
|
|
86
|
+
// authority. This only guards candidates we create; using an existing Host
|
|
87
|
+
// leaves that Host's ownership and lifetime unchanged.
|
|
88
|
+
const invocationOwned = profile.kind === 'local' && (await deps.isTemporaryNpxInstallation());
|
|
86
89
|
const connectInput = {
|
|
87
90
|
rootPath: input.rootPath,
|
|
88
91
|
protocol: { min: RUNTIME_HOST_PROTOCOL_VERSION, max: RUNTIME_HOST_PROTOCOL_VERSION },
|
|
89
92
|
clientInstanceId,
|
|
90
93
|
compositionId: INTERACTIVE_RUNTIME_HOST_COMPOSITION_ID,
|
|
91
94
|
candidateEntrypoint: deps.executionCandidateEntrypoint,
|
|
95
|
+
...(invocationOwned ? { closeOnLauncherExit: true } : {}),
|
|
92
96
|
};
|
|
93
97
|
const connect = async (signal, sshInteraction = 'batch') => {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
const observe = async () => {
|
|
99
|
+
if (profile.kind !== 'local') {
|
|
100
|
+
try {
|
|
101
|
+
const connection = await deps.connectProfile({
|
|
102
|
+
profile,
|
|
103
|
+
...(resolvedProfile.credential ? { credential: resolvedProfile.credential } : {}),
|
|
104
|
+
clientInstanceId,
|
|
105
|
+
sshInteraction,
|
|
106
|
+
...(peerClient ? { peerClient } : {}),
|
|
107
|
+
...(signal ? { signal } : {}),
|
|
108
|
+
});
|
|
109
|
+
return { kind: 'ready', value: connection };
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
if (!(error instanceof RuntimeHostRemoteCompatibilityError))
|
|
113
|
+
throw error;
|
|
114
|
+
return {
|
|
115
|
+
kind: 'blocked',
|
|
116
|
+
blocker: {
|
|
117
|
+
identity: JSON.stringify([profile, error.hostEpoch, error.details]),
|
|
118
|
+
target: {
|
|
119
|
+
name: profile.name,
|
|
120
|
+
location: 'remote',
|
|
121
|
+
rootId: profile.rootId,
|
|
122
|
+
hostEpoch: error.hostEpoch,
|
|
123
|
+
},
|
|
124
|
+
reason: 'upgrade',
|
|
125
|
+
mayExitNaturally: false,
|
|
126
|
+
diagnostic: error.message,
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
let connected = await deps.connectOrSpawn({
|
|
132
|
+
...connectInput,
|
|
101
133
|
...(signal ? { signal } : {}),
|
|
102
134
|
});
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
await connected.connection.close().catch(() => undefined);
|
|
121
|
-
throw new RuntimeHostPermanentReconnectError('RUNTIME_HOST_RECOVERY_REQUIRED: The staged local Runtime Host is Ready, but its installation ownership was not durably committed.');
|
|
135
|
+
if (connected.kind === 'failed' && connected.reason === 'managed_root_requires_operator') {
|
|
136
|
+
await deps.activateLocalManagedHost({
|
|
137
|
+
rootPath: input.rootPath,
|
|
138
|
+
...(signal ? { signal } : {}),
|
|
139
|
+
});
|
|
140
|
+
signal?.throwIfAborted();
|
|
141
|
+
// The installed operator owns launch. Compatibility is still projected
|
|
142
|
+
// by the same handoff journey as an already-running managed Host.
|
|
143
|
+
const activated = await deps.connectActivatedHost(connectInput);
|
|
144
|
+
if (activated.kind === 'unavailable' || activated.kind === 'draining') {
|
|
145
|
+
throw new Error(`The installed Runtime Host was activated but the local CLI could not join it (${activated.kind === 'unavailable' ? activated.reason : activated.kind}). Use a compatible CLI or update the managed Host through its configured operator.`);
|
|
146
|
+
}
|
|
147
|
+
if (activated.kind === 'connected' && signal?.aborted) {
|
|
148
|
+
await activated.connection.close();
|
|
149
|
+
signal.throwIfAborted();
|
|
150
|
+
}
|
|
151
|
+
connected = activated;
|
|
122
152
|
}
|
|
123
|
-
|
|
124
|
-
|
|
153
|
+
if (connected.kind === 'incompatible' || connected.kind === 'upgrade_required') {
|
|
154
|
+
const managed = await deps.resolveManagedAuthority(connected.registration.rootId);
|
|
155
|
+
const record = managed
|
|
156
|
+
? undefined
|
|
157
|
+
: await deps.readDeploymentRecord(connected.registration.rootId);
|
|
158
|
+
let replacement;
|
|
159
|
+
let installation;
|
|
160
|
+
// On-demand managed Hosts are also ephemeral processes. Their durable
|
|
161
|
+
// operator authority, not the process lifetime label, decides who may
|
|
162
|
+
// replace them (including when the Host was already running).
|
|
163
|
+
if (!managed && connected.registration.lifecycleMode === 'ephemeral') {
|
|
164
|
+
try {
|
|
165
|
+
installation = await deps.resolveInstallation();
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
/* No persistent installation authority. */
|
|
169
|
+
}
|
|
170
|
+
const owner = record?.state.kind === 'handoff' ? record.state.from : record?.state.owner;
|
|
171
|
+
if (installation &&
|
|
172
|
+
(!owner ||
|
|
173
|
+
(owner.kind === installation.owner.kind &&
|
|
174
|
+
owner.installationId === installation.owner.installationId))) {
|
|
175
|
+
const expectedInstallation = installation;
|
|
176
|
+
const canInterrupt = record
|
|
177
|
+
? await deps
|
|
178
|
+
.sourceRetirementAvailable({
|
|
179
|
+
rootId: connected.registration.rootId,
|
|
180
|
+
owner: installation.owner,
|
|
181
|
+
source: record.state.selected,
|
|
182
|
+
})
|
|
183
|
+
.catch(() => false)
|
|
184
|
+
: false;
|
|
185
|
+
replacement = {
|
|
186
|
+
kind: record?.state.kind === 'handoff' ? 'repair' : 'replace',
|
|
187
|
+
canReplaceIdle: true,
|
|
188
|
+
canInterrupt,
|
|
189
|
+
execute: async (activeWorkPolicy, _progress, _consent, attemptSignal) => {
|
|
190
|
+
const result = await deps.restartDeployment({
|
|
191
|
+
rootPath: input.rootPath,
|
|
192
|
+
registration: connected.registration,
|
|
193
|
+
activeWorkPolicy,
|
|
194
|
+
expectedInstallation,
|
|
195
|
+
...(attemptSignal ? { signal: attemptSignal } : {}),
|
|
196
|
+
});
|
|
197
|
+
if (result.kind === 'completed')
|
|
198
|
+
return { kind: 'completed' };
|
|
199
|
+
if (result.kind === 'active_work')
|
|
200
|
+
return { kind: 'active_work' };
|
|
201
|
+
if (result.kind === 'changed' ||
|
|
202
|
+
result.kind === 'rejected' ||
|
|
203
|
+
result.kind === 'operator_required')
|
|
204
|
+
return { kind: 'changed' };
|
|
205
|
+
return {
|
|
206
|
+
kind: 'recovery_required',
|
|
207
|
+
diagnostic: `Local service recovery is required at ${result.phase}`,
|
|
208
|
+
};
|
|
209
|
+
},
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return {
|
|
214
|
+
kind: 'blocked',
|
|
215
|
+
blocker: {
|
|
216
|
+
identity: JSON.stringify([
|
|
217
|
+
connected.registration,
|
|
218
|
+
managed?.record,
|
|
219
|
+
record,
|
|
220
|
+
installation,
|
|
221
|
+
]),
|
|
222
|
+
target: {
|
|
223
|
+
name: profile.name,
|
|
224
|
+
location: 'local',
|
|
225
|
+
rootId: connected.registration.rootId,
|
|
226
|
+
hostEpoch: connected.registration.hostEpoch,
|
|
227
|
+
},
|
|
228
|
+
reason: record?.state.kind === 'handoff' ? 'repair' : 'upgrade',
|
|
229
|
+
...(connected.handshake?.activity ? { activity: connected.handshake.activity } : {}),
|
|
230
|
+
mayExitNaturally: !managed &&
|
|
231
|
+
connected.registration.lifecycleMode === 'ephemeral' &&
|
|
232
|
+
connected.handshake?.replacement === 'wait_for_idle_exit',
|
|
233
|
+
...(replacement ? { replacement } : {}),
|
|
234
|
+
},
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
if (connected.kind === 'failed') {
|
|
238
|
+
throw runtimeHostStartupError(connected.reason, connected.diagnostic);
|
|
239
|
+
}
|
|
240
|
+
if (connected.registration.generation?.startsWith('npm-global-handoff:')) {
|
|
241
|
+
const record = await deps.readDeploymentRecord(connected.registration.rootId);
|
|
242
|
+
if (record?.state.kind !== 'owned' || record.state.owner.kind !== 'cli') {
|
|
243
|
+
await connected.connection.close().catch(() => undefined);
|
|
244
|
+
throw new RuntimeHostPermanentReconnectError('RUNTIME_HOST_RECOVERY_REQUIRED: The staged local Runtime Host is Ready, but its installation ownership was not durably committed.');
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return { kind: 'ready', value: connected.connection };
|
|
248
|
+
};
|
|
249
|
+
return runHostHandoff({ observe, signal, openSurface: input.handoffSurface });
|
|
125
250
|
};
|
|
126
251
|
let initialConnection;
|
|
127
252
|
let connection;
|
|
@@ -222,42 +347,6 @@ async function resolveHostProfile(input, deps) {
|
|
|
222
347
|
const catalog = deps.profileCatalog ?? createClientRuntimeHostProfileCatalog(root);
|
|
223
348
|
return catalog.resolve(input.profileId);
|
|
224
349
|
}
|
|
225
|
-
function formatRuntimeHostCliConflict(handshake, registration) {
|
|
226
|
-
const lines = [
|
|
227
|
-
'RUNTIME_HOST_RESTART_REQUIRED: An older Runtime Host is still running and cannot accept this client.',
|
|
228
|
-
`Local Runtime Host: PID ${registration.pid}; lifecycle ${registration.lifecycleMode ?? 'unknown'}; compatibility epoch ${registration.compatibilityEpoch}.`,
|
|
229
|
-
];
|
|
230
|
-
if (registration.lifecycleMode === 'ephemeral') {
|
|
231
|
-
lines.push('The ephemeral Host is not currently idle and cannot be replaced by this Client.');
|
|
232
|
-
}
|
|
233
|
-
else if (registration.lifecycleMode === 'service') {
|
|
234
|
-
lines.push('This service Host is managed by its operator and cannot be replaced by this Client.');
|
|
235
|
-
}
|
|
236
|
-
else {
|
|
237
|
-
lines.push('This Host cannot be replaced by this Client.');
|
|
238
|
-
}
|
|
239
|
-
if (handshake.compatibilityEpoch < RUNTIME_HOST_COMPATIBILITY_EPOCH) {
|
|
240
|
-
lines.push(registration.lifecycleMode === 'service'
|
|
241
|
-
? 'Use the service operator to inspect or upgrade the Host.'
|
|
242
|
-
: 'Use a previous compatible Maka build to inspect the Host and finish or clear any retained work. Stop the Host only after deciding that interruption is safe.');
|
|
243
|
-
}
|
|
244
|
-
else {
|
|
245
|
-
lines.push(`Host protocol ${handshake.protocolMin}-${handshake.protocolMax}; CLI protocol ${RUNTIME_HOST_PROTOCOL_VERSION}.`);
|
|
246
|
-
}
|
|
247
|
-
return lines.join('\n');
|
|
248
|
-
}
|
|
249
|
-
export function shouldRetryRuntimeHostConflict(answer) {
|
|
250
|
-
const normalized = answer.trim().toLowerCase();
|
|
251
|
-
return normalized === 'w' || normalized === 'wait';
|
|
252
|
-
}
|
|
253
|
-
export function resolveRuntimeHostCliConflictDecision(answer, canRestart) {
|
|
254
|
-
const normalized = answer.trim().toLowerCase();
|
|
255
|
-
if (canRestart && (normalized === 'r' || normalized === 'restart'))
|
|
256
|
-
return 'restart';
|
|
257
|
-
if (normalized === 'w' || normalized === 'wait')
|
|
258
|
-
return 'wait';
|
|
259
|
-
return 'cancel';
|
|
260
|
-
}
|
|
261
350
|
export function resolveRuntimeHostCliTarget(catalog, input = {}) {
|
|
262
351
|
const defaultTarget = catalog.defaultTarget;
|
|
263
352
|
const connection = input.connectionSlug
|
|
@@ -76,9 +76,15 @@ export async function resolveRuntimeHostNpmGlobalInstallation(options = {}, over
|
|
|
76
76
|
observedRelease: { version: manifest.version, packageRoot, cliPath },
|
|
77
77
|
};
|
|
78
78
|
}
|
|
79
|
-
export async function isTemporaryNpxInstallation(path, input) {
|
|
79
|
+
export async function isTemporaryNpxInstallation(path = fileURLToPath(new URL('../', import.meta.url)), input = { environment: process.env, homeDir: homedir() }) {
|
|
80
80
|
const canonicalPath = await realpath(path).catch(() => resolve(path));
|
|
81
|
-
const
|
|
81
|
+
const defaultCache = (input.platform ?? process.platform) === 'win32'
|
|
82
|
+
? join(input.environment.LOCALAPPDATA || input.homeDir, 'npm-cache')
|
|
83
|
+
: join(input.homeDir, '.npm');
|
|
84
|
+
const cacheRoots = await Promise.all([
|
|
85
|
+
input.environment.npm_config_cache ?? input.environment.NPM_CONFIG_CACHE,
|
|
86
|
+
defaultCache,
|
|
87
|
+
].flatMap((root) => root ? [realpath(resolve(root, '_npx')).catch(() => resolve(root, '_npx'))] : []));
|
|
82
88
|
return cacheRoots.some((root) => isWithin(root, canonicalPath));
|
|
83
89
|
}
|
|
84
90
|
async function readPackageManifest(url) {
|
package/dist/runtime-host-cli.js
CHANGED
|
@@ -136,6 +136,10 @@ function parseLocalUpdateApply(argv) {
|
|
|
136
136
|
'--target-version',
|
|
137
137
|
'--target-integrity',
|
|
138
138
|
'--target-compatibility',
|
|
139
|
+
'--expected-root-id',
|
|
140
|
+
'--expected-deployment-revision',
|
|
141
|
+
'--expected-owner-installation-id',
|
|
142
|
+
'--expected-host-epoch',
|
|
139
143
|
]);
|
|
140
144
|
for (let index = 0; index < argv.length; index += 1) {
|
|
141
145
|
const argument = argv[index];
|
|
@@ -195,6 +199,28 @@ function parseLocalUpdateApply(argv) {
|
|
|
195
199
|
(!Number.isSafeInteger(targetCompatibility) || targetCompatibility <= 0)) {
|
|
196
200
|
return error('runtime-host local-update-apply compatibility is invalid');
|
|
197
201
|
}
|
|
202
|
+
const expectedValues = [
|
|
203
|
+
'--expected-root-id',
|
|
204
|
+
'--expected-deployment-revision',
|
|
205
|
+
'--expected-owner-installation-id',
|
|
206
|
+
'--expected-host-epoch',
|
|
207
|
+
].map((name) => values.get(name));
|
|
208
|
+
let expectedSource;
|
|
209
|
+
if (expectedValues.some((value) => value !== undefined)) {
|
|
210
|
+
if (!expectedValues.every((value) => value !== undefined && isSafeIdentity(value))) {
|
|
211
|
+
return error('runtime-host local-update-apply requires one complete expected source identity');
|
|
212
|
+
}
|
|
213
|
+
const [rootId, deploymentRevision, ownerInstallationId, hostEpoch] = expectedValues;
|
|
214
|
+
if (!/^[a-f0-9]{64}$/u.test(rootId)) {
|
|
215
|
+
return error('runtime-host local-update-apply expected Root identity is invalid');
|
|
216
|
+
}
|
|
217
|
+
expectedSource = {
|
|
218
|
+
rootId: rootId,
|
|
219
|
+
deploymentRevision: deploymentRevision,
|
|
220
|
+
ownerInstallationId: ownerInstallationId,
|
|
221
|
+
hostEpoch: hostEpoch,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
198
224
|
return {
|
|
199
225
|
kind: 'runtime-host-local-update-apply',
|
|
200
226
|
rootPath,
|
|
@@ -206,6 +232,7 @@ function parseLocalUpdateApply(argv) {
|
|
|
206
232
|
targetIntegrity,
|
|
207
233
|
...(targetCompatibility === undefined ? {} : { targetCompatibility }),
|
|
208
234
|
allowInterruptActiveTasks,
|
|
235
|
+
...(expectedSource ? { expectedSource } : {}),
|
|
209
236
|
};
|
|
210
237
|
}
|
|
211
238
|
function parseLocalUpdateActivate(argv) {
|
|
@@ -553,7 +580,7 @@ function parseServiceManagementCommand(argv) {
|
|
|
553
580
|
},
|
|
554
581
|
}
|
|
555
582
|
: {}),
|
|
556
|
-
...(action === 'update'
|
|
583
|
+
...(action === 'update' || action === 'restart'
|
|
557
584
|
? {
|
|
558
585
|
'--expected-host-json': (value) => {
|
|
559
586
|
if (expectedHost !== undefined)
|
|
@@ -675,10 +702,13 @@ function parseServiceManagementCommand(argv) {
|
|
|
675
702
|
...(allowInterruptActiveTasks ? { allowInterruptActiveTasks: true } : {}),
|
|
676
703
|
};
|
|
677
704
|
}
|
|
705
|
+
if (expectedHost && !options.managedRootId)
|
|
706
|
+
return error('--expected-host-json requires --managed-root-id');
|
|
678
707
|
return {
|
|
679
708
|
kind: 'runtime-host-service-manage',
|
|
680
709
|
action,
|
|
681
710
|
...options,
|
|
711
|
+
...(expectedHost ? { expectedHost } : {}),
|
|
682
712
|
...(clientDataRoot ? { clientDataRoot } : {}),
|
|
683
713
|
...(options.managedRootId ? { managedRootId: options.managedRootId } : {}),
|
|
684
714
|
...(retainManagedDeployment ? { retainManagedDeployment: true } : {}),
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Licensed to the Apache Software Foundation (ASF) under one
|
|
3
|
+
* or more contributor license agreements. See the NOTICE file
|
|
4
|
+
* distributed with this work for additional information
|
|
5
|
+
* regarding copyright ownership. The ASF licenses this file
|
|
6
|
+
* to you under the Apache License, Version 2.0 (the
|
|
7
|
+
* "License"); you may not use this file except in compliance
|
|
8
|
+
* with the License. You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing,
|
|
13
|
+
* software distributed under the License is distributed on an
|
|
14
|
+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
15
|
+
* KIND, either express or implied. See the License for the
|
|
16
|
+
* specific language governing permissions and limitations
|
|
17
|
+
* under the License.
|
|
18
|
+
*/
|
|
19
|
+
import { createInterface } from 'node:readline';
|
|
20
|
+
import { stripVTControlCharacters } from 'node:util';
|
|
21
|
+
import { formatHostHandoff, } from '@maka/runtime-host/client';
|
|
22
|
+
/** One readline lifetime renders the shared decision; it never selects replacement policy. */
|
|
23
|
+
export function createCliHostHandoffSurface(locale, input = process.stdin, output = process.stderr) {
|
|
24
|
+
return (submit) => {
|
|
25
|
+
const readline = createInterface({ input, output, terminal: output.isTTY === true });
|
|
26
|
+
let current;
|
|
27
|
+
let closed = false;
|
|
28
|
+
let ended = false;
|
|
29
|
+
const cancel = () => {
|
|
30
|
+
if (current)
|
|
31
|
+
submit(current.revision, 'cancel');
|
|
32
|
+
};
|
|
33
|
+
readline.on('SIGINT', cancel);
|
|
34
|
+
readline.on('close', () => {
|
|
35
|
+
ended = true;
|
|
36
|
+
if (!closed)
|
|
37
|
+
cancel();
|
|
38
|
+
});
|
|
39
|
+
readline.on('line', (line) => {
|
|
40
|
+
if (!current)
|
|
41
|
+
return;
|
|
42
|
+
const answer = line.trim().toLowerCase();
|
|
43
|
+
const action = answer === 'r' ? 'retry' : answer === 'stop' ? 'interrupt' : 'cancel';
|
|
44
|
+
if (!current.actions.includes(action)) {
|
|
45
|
+
readline.prompt();
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
submit(current.revision, action);
|
|
49
|
+
});
|
|
50
|
+
return {
|
|
51
|
+
update(view) {
|
|
52
|
+
if (closed || current?.revision === view.revision)
|
|
53
|
+
return;
|
|
54
|
+
current = view;
|
|
55
|
+
if (ended) {
|
|
56
|
+
cancel();
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
// Discard partially typed consent when the observed target/consequences change.
|
|
60
|
+
if (output.isTTY)
|
|
61
|
+
readline.write(null, { ctrl: true, name: 'u' });
|
|
62
|
+
const copy = formatHostHandoff(view, locale);
|
|
63
|
+
const text = [copy.title, copy.description, copy.detail, view.diagnostic]
|
|
64
|
+
.filter(Boolean)
|
|
65
|
+
.join('\n');
|
|
66
|
+
output.write('\n' +
|
|
67
|
+
stripVTControlCharacters(text).replace(/[\p{Cc}\p{Cf}]/gu, (character) => character === '\n' ? character : '\uFFFD') +
|
|
68
|
+
'\n');
|
|
69
|
+
if (view.actions.length === 0)
|
|
70
|
+
return;
|
|
71
|
+
const options = copy.actions.map(({ action, label }) => `${action === 'interrupt' ? 'stop' : action === 'retry' ? 'r' : 'Enter'}: ${label}`);
|
|
72
|
+
readline.setPrompt(options.join(' · ') + ' > ');
|
|
73
|
+
readline.prompt();
|
|
74
|
+
},
|
|
75
|
+
close() {
|
|
76
|
+
closed = true;
|
|
77
|
+
readline.close();
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
}
|
|
@@ -64,6 +64,7 @@ export async function runRuntimeHostInstalledUpdateBootstrap(input, overrides =
|
|
|
64
64
|
? {}
|
|
65
65
|
: { targetCompatibility: target.compatibility }),
|
|
66
66
|
allowInterruptActiveTasks: input.allowInterruptActiveTasks,
|
|
67
|
+
...(input.expectedSource ? { expectedSource: input.expectedSource } : {}),
|
|
67
68
|
});
|
|
68
69
|
}
|
|
69
70
|
finally {
|
|
@@ -94,6 +95,18 @@ function launchCoordinator(input) {
|
|
|
94
95
|
? []
|
|
95
96
|
: ['--target-compatibility', String(input.targetCompatibility)]),
|
|
96
97
|
...(input.allowInterruptActiveTasks ? ['--allow-interrupt-active-tasks'] : []),
|
|
98
|
+
...(input.expectedSource
|
|
99
|
+
? [
|
|
100
|
+
'--expected-root-id',
|
|
101
|
+
input.expectedSource.rootId,
|
|
102
|
+
'--expected-deployment-revision',
|
|
103
|
+
input.expectedSource.deploymentRevision,
|
|
104
|
+
'--expected-owner-installation-id',
|
|
105
|
+
input.expectedSource.ownerInstallationId,
|
|
106
|
+
'--expected-host-epoch',
|
|
107
|
+
input.expectedSource.hostEpoch,
|
|
108
|
+
]
|
|
109
|
+
: []),
|
|
97
110
|
];
|
|
98
111
|
return new Promise((resolve, reject) => {
|
|
99
112
|
const child = spawn(process.execPath, args, { stdio: 'inherit', windowsHide: false });
|
|
@@ -22,6 +22,7 @@ import { homedir } from 'node:os';
|
|
|
22
22
|
import { dirname, join } from 'node:path';
|
|
23
23
|
import { pathToFileURL } from 'node:url';
|
|
24
24
|
import { connectExistingRuntimeHost, prepareConnectedRuntimeHostRetirement, waitForRuntimeHostReady, } from '@maka/runtime-host/client';
|
|
25
|
+
import { resolveRuntimeHostManagedDeploymentAuthority, } from '@maka/runtime-host/operator';
|
|
25
26
|
import { INTERACTIVE_RUNTIME_HOST_COMPOSITION_ID, RUNTIME_HOST_PROTOCOL_VERSION, } from '@maka/runtime-host/protocol';
|
|
26
27
|
import { resolveStorageRoot } from '@maka/storage/root-authority';
|
|
27
28
|
import { prepareRuntimeHostNpmGlobalStagedDeployment, reconcilePreparedRuntimeHostNpmGlobalDeployment, } from './runtime-host-local-handoff.js';
|
|
@@ -35,6 +36,7 @@ export async function runRuntimeHostInstalledUpdateCoordinator(input, authorityO
|
|
|
35
36
|
const deps = {
|
|
36
37
|
resolveInstallation: resolveRuntimeHostNpmGlobalInstallation,
|
|
37
38
|
resolveRoot: resolveStorageRoot,
|
|
39
|
+
resolveManagedAuthority: resolveRuntimeHostManagedDeploymentAuthority,
|
|
38
40
|
connectExisting: connectExistingRuntimeHost,
|
|
39
41
|
waitForReady: waitForRuntimeHostReady,
|
|
40
42
|
prepareRetirement: prepareConnectedRuntimeHostRetirement,
|
|
@@ -54,6 +56,14 @@ export async function runRuntimeHostInstalledUpdateCoordinator(input, authorityO
|
|
|
54
56
|
throw new Error(`The installed Maka release changed from ${input.currentVersion} to ${installation.observedRelease.version} before update coordination`);
|
|
55
57
|
}
|
|
56
58
|
const root = await deps.resolveRoot({ path: input.rootPath, kind: 'interactive' });
|
|
59
|
+
if (input.expectedSource &&
|
|
60
|
+
(root.rootId !== input.expectedSource.rootId ||
|
|
61
|
+
installation.owner.installationId !== input.expectedSource.ownerInstallationId)) {
|
|
62
|
+
throw new Error('The observed local Host installation owner changed before update.');
|
|
63
|
+
}
|
|
64
|
+
if (await deps.resolveManagedAuthority(root.rootId)) {
|
|
65
|
+
throw new Error('The managed Runtime Host must be updated through its installed operator.');
|
|
66
|
+
}
|
|
57
67
|
const transactionId = updateTransactionId(root.rootId, installation, input.target);
|
|
58
68
|
return deps.withArchive(input.target, input.archivePath, async ({ packageRoot, archivePath }) => {
|
|
59
69
|
const staged = await deps.prepareStaged({
|
|
@@ -90,7 +100,19 @@ export async function runRuntimeHostInstalledUpdateCoordinator(input, authorityO
|
|
|
90
100
|
return 'target_present';
|
|
91
101
|
};
|
|
92
102
|
const prepare = async (inheritableAuthorityLeaseFd) => {
|
|
103
|
+
if (await deps.resolveManagedAuthority(root.rootId)) {
|
|
104
|
+
throw new Error('The managed Runtime Host must be updated through its installed operator.');
|
|
105
|
+
}
|
|
93
106
|
observation = await observeCurrentHost(input.rootPath, root.rootId, deps);
|
|
107
|
+
// This callback runs under the existing deployment-authority lease.
|
|
108
|
+
// Do not turn the TUI's consent for one observed Host into retirement of
|
|
109
|
+
// a successor that appeared while the archive/coordinator was prepared.
|
|
110
|
+
if (input.expectedSource &&
|
|
111
|
+
observation.registration?.hostEpoch !== input.expectedSource.hostEpoch) {
|
|
112
|
+
await observation.connection?.close();
|
|
113
|
+
observation = {};
|
|
114
|
+
throw new Error('The observed Runtime Host changed before update retirement.');
|
|
115
|
+
}
|
|
94
116
|
if (observation.registration && observation.registration.lifecycleMode !== 'ephemeral') {
|
|
95
117
|
throw new Error('Only an ephemeral local Runtime Host can be updated by this CLI');
|
|
96
118
|
}
|
|
@@ -149,6 +171,17 @@ export async function runRuntimeHostInstalledUpdateCoordinator(input, authorityO
|
|
|
149
171
|
: 'refuse_active_work',
|
|
150
172
|
installation,
|
|
151
173
|
staged,
|
|
174
|
+
...(input.expectedSource
|
|
175
|
+
? {
|
|
176
|
+
expectedOwner: {
|
|
177
|
+
revision: input.expectedSource.deploymentRevision,
|
|
178
|
+
owner: {
|
|
179
|
+
kind: 'cli',
|
|
180
|
+
installationId: input.expectedSource.ownerInstallationId,
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
}
|
|
184
|
+
: {}),
|
|
152
185
|
}, {
|
|
153
186
|
prepareUnownedHostCutover: (_rootId, _target, _staged, _policy, leaseFd) => prepare(leaseFd),
|
|
154
187
|
prepareHostCutover: (_rootId, _selected, _target, _staged, _policy, leaseFd) => prepare(leaseFd),
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
* under the License.
|
|
18
18
|
*/
|
|
19
19
|
import { randomUUID } from 'node:crypto';
|
|
20
|
+
import { withOperatorRetirementCancellation } from './runtime-host-operator-retirement.js';
|
|
20
21
|
import { isDeepStrictEqual } from 'node:util';
|
|
21
22
|
import { resolveExistingStorageRoot, tryAcquireStateRootOwner, } from '@maka/storage/root-authority';
|
|
22
23
|
import { connectExistingRuntimeHost, prepareConnectedRuntimeHostRetirement, RuntimeHostOperationError, RuntimeHostRequestInterruptedError, waitForRuntimeHostReady, } from '@maka/runtime-host/client';
|
|
@@ -173,6 +174,10 @@ function assertRecoveryTarget(expected, rootId, record) {
|
|
|
173
174
|
}
|
|
174
175
|
}
|
|
175
176
|
export async function retireRuntimeHostLifecycleOwner(input) {
|
|
177
|
+
return withOperatorRetirementCancellation((signal) => retireRuntimeHostLifecycleOwnerWithSignal(input, signal));
|
|
178
|
+
}
|
|
179
|
+
async function retireRuntimeHostLifecycleOwnerWithSignal(input, signal) {
|
|
180
|
+
signal?.throwIfAborted();
|
|
176
181
|
if (input.expectedOwner && !input.supervisor) {
|
|
177
182
|
throw new RuntimeHostLifecycleTransactionError('owner_changed', 'A Runtime Host identity fence requires a supervised deployment');
|
|
178
183
|
}
|
|
@@ -188,6 +193,7 @@ export async function retireRuntimeHostLifecycleOwner(input) {
|
|
|
188
193
|
const status = await input.supervisor.status();
|
|
189
194
|
assertExpectedSupervisorOwner(input.expectedOwner, status);
|
|
190
195
|
}
|
|
196
|
+
signal?.throwIfAborted();
|
|
191
197
|
if (input.retireIdleSupervisor !== false)
|
|
192
198
|
await input.supervisor?.retire();
|
|
193
199
|
return { kind: 'retired', owner: idleOwner };
|
|
@@ -212,6 +218,7 @@ export async function retireRuntimeHostLifecycleOwner(input) {
|
|
|
212
218
|
if (status.active && status.pid !== null) {
|
|
213
219
|
if (!input.allowInterruptActiveTasks)
|
|
214
220
|
return { kind: 'active_tasks' };
|
|
221
|
+
signal?.throwIfAborted();
|
|
215
222
|
await input.supervisor.retire();
|
|
216
223
|
return waitForRuntimeHostLifecycleOwner(capability, input.timeoutMs ?? 45_000);
|
|
217
224
|
}
|
|
@@ -234,7 +241,7 @@ export async function retireRuntimeHostLifecycleOwner(input) {
|
|
|
234
241
|
// The exact Root owner and canonical supervisor now agree while the deployment lock is held.
|
|
235
242
|
// This admits retirement of that deployment; a later same-deployment restart is not a new
|
|
236
243
|
// authority, but it must not acquire the Root before the supervisor is retired.
|
|
237
|
-
const prepared = await prepareConnectedRuntimeHostRetirement(connected.connection, input.allowInterruptActiveTasks ? 'interrupt_active_work' : 'refuse_active_work');
|
|
244
|
+
const prepared = await prepareConnectedRuntimeHostRetirement(connected.connection, input.allowInterruptActiveTasks ? 'interrupt_active_work' : 'refuse_active_work', undefined, signal);
|
|
238
245
|
if (prepared.kind === 'active_tasks')
|
|
239
246
|
return prepared;
|
|
240
247
|
if (prepared.pid !== diagnostics.pid) {
|
|
@@ -242,6 +249,7 @@ export async function retireRuntimeHostLifecycleOwner(input) {
|
|
|
242
249
|
}
|
|
243
250
|
}
|
|
244
251
|
catch (error) {
|
|
252
|
+
signal?.throwIfAborted();
|
|
245
253
|
if (!isRuntimeHostRetirementUnavailable(error) || !input.supervisor)
|
|
246
254
|
throw error;
|
|
247
255
|
if (!input.allowInterruptActiveTasks)
|