maka-agent 0.2.0-dev.23.20260906 → 0.2.0-dev.25.20260908
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 +38 -19
- package/README.zh-CN.md +34 -19
- 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-tools.js +6 -0
- package/dist/pi-transcript.js +5 -12
- package/dist/pi-tui-layout.js +56 -10
- package/dist/pi-tui-pickers.js +102 -1
- package/dist/pi-tui-runner.js +230 -46
- package/dist/pi-tui-todo.js +221 -0
- package/dist/pi-tui-transcript-viewer.js +134 -7
- package/dist/runtime-host-cli-context.js +224 -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 +106 -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 +48 -5
- 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 -3
- package/dist/runtime-host-session-channel.js +13 -0
- package/dist/runtime-host-session-driver.js +46 -26
- 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 +219 -6
- package/dist/tui-host-owner-copy.js +24 -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/agent-run.js +1 -0
- package/node_modules/@maka/core/dist/attachments.js +3 -0
- package/node_modules/@maka/core/dist/canonical-json.js +27 -0
- package/node_modules/@maka/core/dist/canonical-runtime-event.js +3 -1
- package/node_modules/@maka/core/dist/capabilities.js +35 -0
- package/node_modules/@maka/core/dist/computer-use.js +0 -1
- package/node_modules/@maka/core/dist/display-redaction.js +18 -0
- package/node_modules/@maka/core/dist/llm-connections.js +13 -0
- package/node_modules/@maka/core/dist/model-call-attempt.js +2 -0
- 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 +7 -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 +9 -2
- package/node_modules/@maka/core/dist/redaction.js +35 -0
- package/node_modules/@maka/core/dist/run-composition.js +77 -2
- 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.js +84 -56
- package/node_modules/@maka/core/dist/slash-command-catalog.js +3 -0
- package/node_modules/@maka/core/dist/tool-result-archive-evidence.js +20 -0
- package/node_modules/@maka/core/dist/tool-result-record-schema.js +2 -1
- package/node_modules/@maka/core/dist/usage-record-schema.js +26 -35
- package/node_modules/@maka/core/package.json +5 -0
- package/node_modules/@maka/eval/dist/experiment-directory.js +4 -13
- package/node_modules/@maka/mcp/dist/index.js +131 -1
- package/node_modules/@maka/runtime/dist/agent-run-inspect.js +0 -13
- package/node_modules/@maka/runtime/dist/agent-run.js +351 -105
- package/node_modules/@maka/runtime/dist/ai-sdk-backend.js +29 -13
- package/node_modules/@maka/runtime/dist/ai-sdk-compaction.js +109 -34
- package/node_modules/@maka/runtime/dist/ai-sdk-turn.js +184 -183
- package/node_modules/@maka/runtime/dist/archive-read-tool.js +2 -2
- package/node_modules/@maka/runtime/dist/context-budget-helpers.js +0 -16
- package/node_modules/@maka/runtime/dist/context-budget.js +14 -9
- package/node_modules/@maka/runtime/dist/conversation-copy.js +158 -15
- package/node_modules/@maka/runtime/dist/github-copilot-oauth-enrollment.js +3 -3
- 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 +174 -0
- package/node_modules/@maka/runtime/dist/mcp-tools.js +7 -2
- 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 +49 -2
- package/node_modules/@maka/runtime/dist/model-history.js +0 -112
- 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/plugin-runtime.js +20 -10
- package/node_modules/@maka/runtime/dist/plugin-tool-service.js +205 -0
- package/node_modules/@maka/runtime/dist/provider-error-classification.js +84 -123
- package/node_modules/@maka/runtime/dist/provider-request-telemetry.js +76 -109
- package/node_modules/@maka/runtime/dist/request-shape.js +23 -0
- package/node_modules/@maka/runtime/dist/run-handoff-gate.js +97 -0
- package/node_modules/@maka/runtime/dist/runtime-event-backfill.js +83 -55
- package/node_modules/@maka/runtime/dist/runtime-event-read-model.js +121 -159
- package/node_modules/@maka/runtime/dist/runtime-kernel.js +157 -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/stream-graph-coordinator.js +21 -0
- package/node_modules/@maka/runtime/dist/stream-graph-supervisor-tools.js +0 -5
- package/node_modules/@maka/runtime/dist/subscription-model-fetch.js +3 -4
- package/node_modules/@maka/runtime/dist/test-connection.js +16 -17
- package/node_modules/@maka/runtime/dist/tool-activation-identity.js +41 -0
- package/node_modules/@maka/runtime/dist/tool-availability.js +11 -1
- package/node_modules/@maka/runtime/dist/tool-result-archive-encoding.js +38 -0
- package/node_modules/@maka/runtime/dist/tool-result-archive-resource.js +63 -1
- package/node_modules/@maka/runtime/dist/tool-result-archive-transition.js +42 -12
- package/node_modules/@maka/runtime/dist/tool-result-archive.js +27 -4
- package/node_modules/@maka/runtime/dist/tool-runtime.js +3 -61
- package/node_modules/@maka/runtime/package.json +2 -0
- package/node_modules/@maka/runtime-host/dist/adapter/session-projector.js +35 -0
- 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 +179 -0
- package/node_modules/@maka/runtime-host/dist/client/host-handoff.js +275 -0
- package/node_modules/@maka/runtime-host/dist/client/host-retirement.js +26 -5
- package/node_modules/@maka/runtime-host/dist/client/index.js +2 -0
- package/node_modules/@maka/runtime-host/dist/client/launcher.js +3 -0
- 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 +46 -5
- package/node_modules/@maka/runtime-host/dist/control/endpoint.js +1 -3
- package/node_modules/@maka/runtime-host/dist/node-error.js +22 -0
- package/node_modules/@maka/runtime-host/dist/operator/index.js +2 -1
- package/node_modules/@maka/runtime-host/dist/operator/managed-deployment.js +1 -3
- package/node_modules/@maka/runtime-host/dist/operator/retirement-cancellation.js +20 -0
- package/node_modules/@maka/runtime-host/dist/peer-mesh/limits.js +0 -1
- package/node_modules/@maka/runtime-host/dist/peer-mesh/model.js +1 -1
- package/node_modules/@maka/runtime-host/dist/peer-reachability/publisher.js +1 -3
- package/node_modules/@maka/runtime-host/dist/protocol/host-status.js +21 -12
- package/node_modules/@maka/runtime-host/dist/protocol/index.js +29 -1
- package/node_modules/@maka/runtime-host/dist/protocol/operations.js +1 -0
- package/node_modules/@maka/runtime-host/dist/protocol/plan.js +1 -0
- package/node_modules/@maka/runtime-host/dist/protocol/plugin-platform.js +27 -5
- package/node_modules/@maka/runtime-host/dist/protocol/project-catalog.js +2 -2
- 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 +31 -2
- package/node_modules/@maka/runtime-host/dist/server/access-credential-store.js +1 -0
- package/node_modules/@maka/runtime-host/dist/server/agent-graph-coordinator.js +0 -3
- package/node_modules/@maka/runtime-host/dist/server/canonical-turn-snapshot.js +19 -4
- package/node_modules/@maka/runtime-host/dist/server/connection-session.js +7 -6
- package/node_modules/@maka/runtime-host/dist/server/execution-artifacts.js +32 -32
- package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +144 -33
- 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 +45 -16
- package/node_modules/@maka/runtime-host/dist/server/goal-coordinator.js +40 -1
- package/node_modules/@maka/runtime-host/dist/server/host-kernel.js +96 -17
- 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/interactive-run-composer.js +47 -22
- package/node_modules/@maka/runtime-host/dist/server/message-coordinator.js +76 -6
- package/node_modules/@maka/runtime-host/dist/server/plugin-platform-coordinator.js +6 -0
- package/node_modules/@maka/runtime-host/dist/server/plugin-platform.js +6 -0
- package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +325 -73
- 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 +50 -7
- package/node_modules/@maka/runtime-host/dist/server/session-continuity-coordinator.js +87 -25
- package/node_modules/@maka/runtime-host/dist/server/session-retirement-coordinator.js +5 -2
- package/node_modules/@maka/runtime-host/dist/server/session-revision-coordinator.js +12 -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/workhub-coordination-action-gate.js +20 -2
- package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-coordinator.js +13 -4
- package/node_modules/@maka/runtime-host/dist/server/workhub-message-attachments.js +46 -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 -5
- package/node_modules/@maka/storage/dist/codex-session-adapter.js +284 -198
- package/node_modules/@maka/storage/dist/context-offload-snapshot.js +325 -0
- package/node_modules/@maka/storage/dist/execution-stores.js +9 -7
- package/node_modules/@maka/storage/dist/file-session-repository.js +1012 -0
- package/node_modules/@maka/storage/dist/git-exec.js +61 -0
- package/node_modules/@maka/storage/dist/git-worktree-child-executor.js +8 -35
- package/node_modules/@maka/storage/dist/goal-authority.js +2 -2
- package/node_modules/@maka/storage/dist/interaction-store.js +2 -2
- 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/project-catalog.js +2 -23
- package/node_modules/@maka/storage/dist/runtime-event-invariants.js +2 -2
- package/node_modules/@maka/storage/dist/runtime-event-persistence.js +1 -0
- package/node_modules/@maka/storage/dist/runtime-policy/connection-catalog-document.js +8 -3
- package/node_modules/@maka/storage/dist/runtime-transcript-query.js +308 -0
- package/node_modules/@maka/storage/dist/scheduled-task-store.js +249 -283
- package/node_modules/@maka/storage/dist/session-bundle-manifest.js +5 -17
- package/node_modules/@maka/storage/dist/session-bundle-policy.js +26 -5
- package/node_modules/@maka/storage/dist/session-copy-cleanup.js +2 -1
- 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 +7 -1
- package/node_modules/@maka/storage/dist/sqlite-runtime-schema.js +44 -1
- package/node_modules/@maka/storage/dist/sqlite-runtime-store.js +130 -27
- package/node_modules/@maka/storage/dist/sqlite-session-metadata-store.js +109 -892
- package/node_modules/@maka/storage/dist/storage-id.js +28 -0
- package/node_modules/@maka/storage/dist/tool-result-archive-evidence.js +138 -0
- package/node_modules/@maka/storage/dist/workspace-identity.js +2 -15
- package/node_modules/@maka/storage/dist/workspace-version-authority-internal.js +1 -8
- package/node_modules/@maka/storage/package.json +2 -0
- package/package.json +1 -1
|
@@ -0,0 +1,106 @@
|
|
|
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 { clearLine, createInterface, cursorTo } from 'node:readline';
|
|
20
|
+
import { stripVTControlCharacters } from 'node:util';
|
|
21
|
+
import { formatHostHandoff, } from '@maka/runtime-host/client';
|
|
22
|
+
/** The surface owns terminal input; replacement policy stays in the shared handoff. */
|
|
23
|
+
export function createCliHostHandoffSurface(locale, input = process.stdin, output = process.stderr) {
|
|
24
|
+
return (submit) => {
|
|
25
|
+
let readline;
|
|
26
|
+
let current;
|
|
27
|
+
let closed = false;
|
|
28
|
+
let ended = false;
|
|
29
|
+
const releaseReader = () => {
|
|
30
|
+
const previous = readline;
|
|
31
|
+
readline = undefined;
|
|
32
|
+
previous?.close();
|
|
33
|
+
};
|
|
34
|
+
const cancel = () => {
|
|
35
|
+
if (current)
|
|
36
|
+
submit(current.revision, 'cancel');
|
|
37
|
+
};
|
|
38
|
+
return {
|
|
39
|
+
update(view) {
|
|
40
|
+
if (closed || current?.revision === view.revision)
|
|
41
|
+
return;
|
|
42
|
+
current = view;
|
|
43
|
+
if (ended) {
|
|
44
|
+
cancel();
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
// A new observation gets a new input buffer. Ctrl+U leaves text after
|
|
48
|
+
// the cursor intact and is ignored by readline in TERM=dumb.
|
|
49
|
+
releaseReader();
|
|
50
|
+
if (output.isTTY) {
|
|
51
|
+
clearLine(output, 0);
|
|
52
|
+
cursorTo(output, 0);
|
|
53
|
+
}
|
|
54
|
+
const copy = formatHostHandoff(view, locale);
|
|
55
|
+
const text = [copy.title, copy.description, copy.detail, view.diagnostic]
|
|
56
|
+
.filter(Boolean)
|
|
57
|
+
.join('\n');
|
|
58
|
+
output.write('\n' +
|
|
59
|
+
stripVTControlCharacters(text).replace(/[\p{Cc}\p{Cf}]/gu, (character) => character === '\n' ? character : '\uFFFD') +
|
|
60
|
+
'\n');
|
|
61
|
+
if (view.actions.length === 0)
|
|
62
|
+
return;
|
|
63
|
+
const options = copy.actions.map(({ action, label }) => `${action === 'interrupt' ? 'stop' : action === 'retry' ? 'r' : 'Enter'}: ${label}`);
|
|
64
|
+
// Create the reader only after the explanation is visible, with the
|
|
65
|
+
// final action prompt already installed. Never draw readline's default >.
|
|
66
|
+
const reader = createInterface({
|
|
67
|
+
input,
|
|
68
|
+
output,
|
|
69
|
+
terminal: output.isTTY === true,
|
|
70
|
+
prompt: options.join(' · ') + ' > ',
|
|
71
|
+
});
|
|
72
|
+
readline = reader;
|
|
73
|
+
reader.on('SIGINT', cancel);
|
|
74
|
+
reader.on('close', () => {
|
|
75
|
+
if (readline !== reader)
|
|
76
|
+
return;
|
|
77
|
+
ended = true;
|
|
78
|
+
if (!closed)
|
|
79
|
+
cancel();
|
|
80
|
+
});
|
|
81
|
+
reader.on('line', (line) => {
|
|
82
|
+
if (readline !== reader || current?.revision !== view.revision)
|
|
83
|
+
return;
|
|
84
|
+
const answer = line.trim().toLowerCase();
|
|
85
|
+
const action = answer === 'r'
|
|
86
|
+
? 'retry'
|
|
87
|
+
: answer === 'stop'
|
|
88
|
+
? 'interrupt'
|
|
89
|
+
: answer === ''
|
|
90
|
+
? 'cancel'
|
|
91
|
+
: undefined;
|
|
92
|
+
if (!action || !view.actions.includes(action)) {
|
|
93
|
+
reader.prompt();
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
submit(view.revision, action);
|
|
97
|
+
});
|
|
98
|
+
reader.prompt();
|
|
99
|
+
},
|
|
100
|
+
close() {
|
|
101
|
+
closed = true;
|
|
102
|
+
releaseReader();
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
}
|
|
@@ -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)
|
|
@@ -20,9 +20,10 @@ import { createHash, randomUUID } from 'node:crypto';
|
|
|
20
20
|
import { realpath, stat } from 'node:fs/promises';
|
|
21
21
|
import { homedir } from 'node:os';
|
|
22
22
|
import { dirname, join, posix, resolve, win32 } from 'node:path';
|
|
23
|
-
import {
|
|
23
|
+
import { isDeepStrictEqual } from 'node:util';
|
|
24
|
+
import { claimLocalHostProcessDeployment, handoffLocalHostProcessDeployment, readLocalHostDeploymentRecord, resolveRuntimeHostManagedDeploymentAuthority, } from '@maka/runtime-host/operator';
|
|
24
25
|
import { compareProductReleaseVersions } from '@maka/runtime-host/operator/update-package-evidence';
|
|
25
|
-
import { connectExistingRuntimeHost } from '@maka/runtime-host/client';
|
|
26
|
+
import { connectExistingRuntimeHost, forceTerminateObservedRegisteredRuntimeHost, } from '@maka/runtime-host/client';
|
|
26
27
|
import { INTERACTIVE_RUNTIME_HOST_COMPOSITION_ID, RUNTIME_HOST_PROTOCOL_VERSION, } from '@maka/runtime-host/protocol';
|
|
27
28
|
import { resolveRuntimeHostNpmGlobalInstallation, } from './runtime-host-cli-installation.js';
|
|
28
29
|
import { openRuntimeHostPackageDeployment, prepareRuntimeHostPackageDeployment, resolveRuntimeHostPackageCliPath, } from './runtime-host-package-deployment.js';
|
|
@@ -53,6 +54,7 @@ export class RuntimeHostLocalHandoffError extends Error {
|
|
|
53
54
|
* retain only the bounded true-idle takeover adapter.
|
|
54
55
|
*/
|
|
55
56
|
export async function restartRuntimeHostNpmGlobalDeployment(input, authorityOptions = {}, overrides = {}) {
|
|
57
|
+
input.signal?.throwIfAborted();
|
|
56
58
|
if (input.registration.lifecycleMode !== 'ephemeral') {
|
|
57
59
|
return {
|
|
58
60
|
kind: 'operator_required',
|
|
@@ -64,15 +66,23 @@ export async function restartRuntimeHostNpmGlobalDeployment(input, authorityOpti
|
|
|
64
66
|
withPackage: withRuntimeHostRegistryUpdatePackage,
|
|
65
67
|
prepareDeployment: prepareRuntimeHostPackageDeployment,
|
|
66
68
|
readRecord: readLocalHostDeploymentRecord,
|
|
69
|
+
resolveManagedAuthority: resolveRuntimeHostManagedDeploymentAuthority,
|
|
67
70
|
claim: claimLocalHostProcessDeployment,
|
|
68
71
|
handoff: handoffLocalHostProcessDeployment,
|
|
69
72
|
resolveCandidate: resolveRuntimeHostRegistryUpdateCandidate,
|
|
70
73
|
connectExisting: connectExistingRuntimeHost,
|
|
71
74
|
activateTarget: launchRuntimeHostTargetActivator,
|
|
72
75
|
retireSource: launchRuntimeHostLocalSourceRetirement,
|
|
76
|
+
terminateObservedHost: forceTerminateObservedRegisteredRuntimeHost,
|
|
73
77
|
...overrides,
|
|
74
78
|
};
|
|
79
|
+
if (await deps.resolveManagedAuthority(input.registration.rootId)) {
|
|
80
|
+
return { kind: 'operator_required', reason: 'unowned_host' };
|
|
81
|
+
}
|
|
75
82
|
const installation = await deps.resolveInstallation(input.installationOptions);
|
|
83
|
+
if (input.expectedInstallation && !isDeepStrictEqual(installation, input.expectedInstallation)) {
|
|
84
|
+
return { kind: 'changed' };
|
|
85
|
+
}
|
|
76
86
|
const current = await deps.readRecord(input.registration.rootId, authorityOptions);
|
|
77
87
|
const sourceOwner = current?.state.kind === 'handoff' ? current.state.from : current?.state.owner;
|
|
78
88
|
if (sourceOwner && !sameOwner(sourceOwner, installation.owner)) {
|
|
@@ -130,9 +140,13 @@ export async function restartRuntimeHostNpmGlobalDeployment(input, authorityOpti
|
|
|
130
140
|
return 'target_present';
|
|
131
141
|
};
|
|
132
142
|
const prepare = async (rootId, selectedDeployment, stagedTarget, inheritableAuthorityLeaseFd) => {
|
|
143
|
+
input.signal?.throwIfAborted();
|
|
133
144
|
if (rootId !== input.registration.rootId) {
|
|
134
145
|
throw new RuntimeHostLocalHandoffError('root_changed', 'The local Runtime Host State Root changed before restart');
|
|
135
146
|
}
|
|
147
|
+
if (await deps.resolveManagedAuthority(rootId)) {
|
|
148
|
+
throw new Error('The managed Runtime Host requires its operator to perform replacement');
|
|
149
|
+
}
|
|
136
150
|
const observed = await deps.connectExisting({
|
|
137
151
|
rootPath: input.rootPath,
|
|
138
152
|
protocol: { min: RUNTIME_HOST_PROTOCOL_VERSION, max: RUNTIME_HOST_PROTOCOL_VERSION },
|
|
@@ -179,6 +193,7 @@ export async function restartRuntimeHostNpmGlobalDeployment(input, authorityOpti
|
|
|
179
193
|
expectedHostEpoch: registration.hostEpoch,
|
|
180
194
|
activeWorkPolicy,
|
|
181
195
|
inheritableAuthorityLeaseFd,
|
|
196
|
+
...(input.signal ? { signal: input.signal } : {}),
|
|
182
197
|
});
|
|
183
198
|
if (retired === 'active_work')
|
|
184
199
|
return { kind: 'active_work' };
|
|
@@ -191,6 +206,15 @@ export async function restartRuntimeHostNpmGlobalDeployment(input, authorityOpti
|
|
|
191
206
|
}
|
|
192
207
|
return { kind: 'target_present' };
|
|
193
208
|
}
|
|
209
|
+
// Older source packages cannot negotiate retirement. Explicit consent may
|
|
210
|
+
// use Desktop's exact-process recovery, inside this existing deployment
|
|
211
|
+
// transaction and only after staging the verified successor.
|
|
212
|
+
if (activeWorkPolicy === 'interrupt_active_work' && input.processIdentity) {
|
|
213
|
+
input.signal?.throwIfAborted();
|
|
214
|
+
const stopped = await deps.terminateObservedHost({ rootPath: input.rootPath, registration: input.registration }, { processIdentity: input.processIdentity, isCurrent: () => !input.signal?.aborted });
|
|
215
|
+
if (!stopped)
|
|
216
|
+
throw new Error('The observed Runtime Host changed before it could be stopped safely');
|
|
217
|
+
}
|
|
194
218
|
const activated = await activateExactTarget(rootId, stagedTarget, inheritableAuthorityLeaseFd, registration.hostEpoch);
|
|
195
219
|
if (activated === 'active_work')
|
|
196
220
|
return { kind: 'active_work' };
|
|
@@ -286,6 +310,12 @@ export async function reconcilePreparedRuntimeHostNpmGlobalDeployment(request, l
|
|
|
286
310
|
return request.staged;
|
|
287
311
|
};
|
|
288
312
|
const current = await overrides.readRecord(request.rootId, authorityOptions);
|
|
313
|
+
if (request.expectedOwner &&
|
|
314
|
+
(current?.state.kind !== 'owned' ||
|
|
315
|
+
current.revision !== request.expectedOwner.revision ||
|
|
316
|
+
!sameOwner(current.state.owner, request.expectedOwner.owner))) {
|
|
317
|
+
throw new RuntimeHostLocalHandoffError('source_owner_mismatch', 'The observed local Host owner changed before update reconciliation');
|
|
318
|
+
}
|
|
289
319
|
if (!current) {
|
|
290
320
|
return overrides.claim({
|
|
291
321
|
rootId: request.rootId,
|
|
@@ -297,9 +327,12 @@ export async function reconcilePreparedRuntimeHostNpmGlobalDeployment(request, l
|
|
|
297
327
|
}
|
|
298
328
|
return overrides.handoff({
|
|
299
329
|
rootId: request.rootId,
|
|
300
|
-
|
|
330
|
+
// The same captured CAS token is checked again by begin_handoff under
|
|
331
|
+
// the authority lock. Never substitute a newly observed owner revision.
|
|
332
|
+
expectedRevision: request.expectedOwner?.revision ?? current.revision,
|
|
301
333
|
transactionId: current.state.kind === 'handoff' ? current.state.transactionId : request.transactionId,
|
|
302
|
-
from:
|
|
334
|
+
from: request.expectedOwner?.owner ??
|
|
335
|
+
(current.state.kind === 'handoff' ? current.state.from : current.state.owner),
|
|
303
336
|
to: request.installation.owner,
|
|
304
337
|
target: request.target,
|
|
305
338
|
activeWorkPolicy: request.activeWorkPolicy,
|
|
@@ -331,7 +364,7 @@ export async function prepareRuntimeHostNpmGlobalStagedDeployment(input, pathOpt
|
|
|
331
364
|
launchGeneration: launchGeneration(input.transactionId, input.target),
|
|
332
365
|
};
|
|
333
366
|
}
|
|
334
|
-
async function openRuntimeHostNpmGlobalStagedDeployment(input, pathOptions = {}) {
|
|
367
|
+
export async function openRuntimeHostNpmGlobalStagedDeployment(input, pathOptions = {}) {
|
|
335
368
|
const deploymentRoot = resolveRuntimeHostLocalCliDeploymentRoot(input.rootId, input.owner, pathOptions);
|
|
336
369
|
const staged = await openRuntimeHostPackageDeployment({
|
|
337
370
|
deploymentRoot,
|
|
@@ -379,6 +412,16 @@ async function requireCandidateEntrypoint(packageRoot) {
|
|
|
379
412
|
}
|
|
380
413
|
return candidate;
|
|
381
414
|
}
|
|
415
|
+
/** Read-only capability check against the same verified source package the transaction uses. */
|
|
416
|
+
export async function runtimeHostNpmGlobalSourceRetirementAvailable(input) {
|
|
417
|
+
const source = await openRuntimeHostNpmGlobalStagedDeployment({
|
|
418
|
+
rootId: input.rootId,
|
|
419
|
+
owner: input.owner,
|
|
420
|
+
target: input.source,
|
|
421
|
+
transactionId: 'source-capability-probe',
|
|
422
|
+
}, input.deploymentPathOptions);
|
|
423
|
+
return hasSourceRetirementHelper(source);
|
|
424
|
+
}
|
|
382
425
|
async function hasSourceRetirementHelper(source) {
|
|
383
426
|
try {
|
|
384
427
|
return (await stat(join(dirname(source.cliPath), 'runtime-host-local-source-retirement.js'))).isFile();
|
|
@@ -0,0 +1,84 @@
|
|
|
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 { execFile } from 'node:child_process';
|
|
20
|
+
import { access } from 'node:fs/promises';
|
|
21
|
+
import { join } from 'node:path';
|
|
22
|
+
import { promisify } from 'node:util';
|
|
23
|
+
import { resolveStorageRoot } from '@maka/storage/root-authority';
|
|
24
|
+
import { runtimeHostStartupError } from '@maka/runtime-host/client';
|
|
25
|
+
import { decodeRuntimeHostActivationFrame, readRuntimeHostManagedDeploymentConfig, RUNTIME_HOST_ACTIVATION_FRAME_MAX_BYTES, runtimeHostManagedOperatorCommand, runtimeHostOperatorInvocation, } from '@maka/runtime-host/operator';
|
|
26
|
+
const run = promisify(execFile);
|
|
27
|
+
/** Invoke the installed owner; never launch the calling CLI's candidate as the owner. */
|
|
28
|
+
export async function activateLocalManagedRuntimeHost(input) {
|
|
29
|
+
input.signal?.throwIfAborted();
|
|
30
|
+
const capability = await resolveStorageRoot({ path: input.rootPath, kind: 'interactive' });
|
|
31
|
+
const config = await readRuntimeHostManagedDeploymentConfig(capability);
|
|
32
|
+
if (!config || config.lifecycle.mode !== 'on_demand') {
|
|
33
|
+
throw runtimeHostStartupError('managed_root_requires_operator');
|
|
34
|
+
}
|
|
35
|
+
const operator = runtimeHostManagedOperatorCommand(config, process.platform === 'win32' ? 'win32' : 'posix');
|
|
36
|
+
let invocation = runtimeHostOperatorInvocation(operator, [
|
|
37
|
+
'activate',
|
|
38
|
+
'--framed',
|
|
39
|
+
'--root-id',
|
|
40
|
+
capability.rootId,
|
|
41
|
+
]);
|
|
42
|
+
// Older installations have only the stable POSIX operator executable.
|
|
43
|
+
try {
|
|
44
|
+
await access(operator.modulePath);
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
if (error.code !== 'ENOENT' || process.platform === 'win32') {
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
invocation = {
|
|
51
|
+
executable: join(config.deploymentRoot, 'operator'),
|
|
52
|
+
args: invocation.args.slice(1),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
const result = await run(invocation.executable, [...invocation.args], {
|
|
56
|
+
encoding: 'utf8',
|
|
57
|
+
timeout: 120_000,
|
|
58
|
+
killSignal: 'SIGKILL',
|
|
59
|
+
maxBuffer: RUNTIME_HOST_ACTIVATION_FRAME_MAX_BYTES + 256,
|
|
60
|
+
...(input.signal ? { signal: input.signal } : {}),
|
|
61
|
+
}).catch((error) => {
|
|
62
|
+
input.signal?.throwIfAborted();
|
|
63
|
+
// Operators report a framed diagnostic and exit nonzero on expected failures.
|
|
64
|
+
if (error instanceof Error && 'stdout' in error && typeof error.stdout === 'string') {
|
|
65
|
+
const frame = decodeSingleActivationFrame(error.stdout);
|
|
66
|
+
if (frame?.kind === 'error')
|
|
67
|
+
throw new Error(frame.error.message, { cause: error });
|
|
68
|
+
}
|
|
69
|
+
throw error;
|
|
70
|
+
});
|
|
71
|
+
input.signal?.throwIfAborted();
|
|
72
|
+
const frame = decodeSingleActivationFrame(result.stdout);
|
|
73
|
+
if (!frame)
|
|
74
|
+
throw new Error('The installed Runtime Host operator returned an invalid activation result');
|
|
75
|
+
if (frame.kind === 'error')
|
|
76
|
+
throw new Error(frame.error.message);
|
|
77
|
+
if (frame.rootId !== capability.rootId || frame.deploymentId !== config.deploymentId) {
|
|
78
|
+
throw new Error('The installed Runtime Host operator activated a different deployment');
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function decodeSingleActivationFrame(stdout) {
|
|
82
|
+
const line = stdout.replace(/\r?\n$/u, '');
|
|
83
|
+
return /[\r\n]/u.test(line) ? undefined : decodeRuntimeHostActivationFrame(line);
|
|
84
|
+
}
|
|
@@ -58,6 +58,7 @@ export async function runRuntimeHostLocalSourceRetirement(input, overrides = {})
|
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
export function launchRuntimeHostLocalSourceRetirement(input) {
|
|
61
|
+
input.signal?.throwIfAborted();
|
|
61
62
|
const args = [
|
|
62
63
|
input.sourceCliPath,
|
|
63
64
|
'runtime-host',
|
|
@@ -81,9 +82,24 @@ export function launchRuntimeHostLocalSourceRetirement(input) {
|
|
|
81
82
|
killSignal: 'SIGKILL',
|
|
82
83
|
windowsHide: false,
|
|
83
84
|
});
|
|
84
|
-
|
|
85
|
+
// Only this retirement client is stopped, never the parent that owns the
|
|
86
|
+
// durable deployment transaction. Wait for its fd/connection to close.
|
|
87
|
+
const cancel = () => {
|
|
88
|
+
child.kill('SIGKILL');
|
|
89
|
+
};
|
|
90
|
+
input.signal?.addEventListener('abort', cancel, { once: true });
|
|
91
|
+
if (input.signal?.aborted)
|
|
92
|
+
cancel();
|
|
93
|
+
let spawnError;
|
|
94
|
+
child.once('error', (error) => {
|
|
95
|
+
spawnError = error;
|
|
96
|
+
});
|
|
85
97
|
child.once('close', (code, signal) => {
|
|
86
|
-
|
|
98
|
+
input.signal?.removeEventListener('abort', cancel);
|
|
99
|
+
if (spawnError) {
|
|
100
|
+
reject(spawnError);
|
|
101
|
+
}
|
|
102
|
+
else if (signal) {
|
|
87
103
|
reject(new Error(`Maka source retirement helper exited on ${signal}`));
|
|
88
104
|
}
|
|
89
105
|
else if (code === 0) {
|
|
@@ -77,6 +77,7 @@ export async function manageRuntimeHostManagedLifecycle(rootId, input, dependenc
|
|
|
77
77
|
}
|
|
78
78
|
if (input.action === 'restart') {
|
|
79
79
|
const retirement = await retireRuntimeHostLifecycleOwner({
|
|
80
|
+
...(input.expectedHost ? { expectedOwner: input.expectedHost } : {}),
|
|
80
81
|
rootPath: config.root.path,
|
|
81
82
|
rootId,
|
|
82
83
|
supervisor: provider.supervisor,
|
|
@@ -0,0 +1,50 @@
|
|
|
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 { RUNTIME_HOST_OPERATOR_RETIREMENT_CANCELLATION_ENV } from '@maka/runtime-host/operator';
|
|
20
|
+
/** Consume the operator's one-shot cancellation channel only around reversible retirement. */
|
|
21
|
+
export function withOperatorRetirementCancellation(operation) {
|
|
22
|
+
if (process.env[RUNTIME_HOST_OPERATOR_RETIREMENT_CANCELLATION_ENV] !== '1') {
|
|
23
|
+
return operation(undefined);
|
|
24
|
+
}
|
|
25
|
+
// Recovery may retire another owner later. It must finish independently of
|
|
26
|
+
// the original user's cancellation, and activators must not inherit this channel.
|
|
27
|
+
delete process.env[RUNTIME_HOST_OPERATOR_RETIREMENT_CANCELLATION_ENV];
|
|
28
|
+
return withRetirementCancellation(process.stdin, operation);
|
|
29
|
+
}
|
|
30
|
+
export async function withRetirementCancellation(input, operation) {
|
|
31
|
+
const controller = new AbortController();
|
|
32
|
+
const cancel = () => controller.abort(new Error('Runtime Host retirement was cancelled'));
|
|
33
|
+
input.once('end', cancel);
|
|
34
|
+
input.once('error', cancel);
|
|
35
|
+
input.once('close', cancel);
|
|
36
|
+
input.resume();
|
|
37
|
+
if (input.readableEnded || input.destroyed)
|
|
38
|
+
cancel();
|
|
39
|
+
try {
|
|
40
|
+
return await operation(controller.signal);
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
input.removeListener('end', cancel);
|
|
44
|
+
input.removeListener('error', cancel);
|
|
45
|
+
input.removeListener('close', cancel);
|
|
46
|
+
input.pause();
|
|
47
|
+
// A parent's still-open cancellation pipe must not keep the completed CLI alive.
|
|
48
|
+
input.unref?.();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -59,6 +59,9 @@ function runtimeHostServiceManagerDeps(overrides) {
|
|
|
59
59
|
};
|
|
60
60
|
}
|
|
61
61
|
export async function manageRuntimeHostService(input, backend, overrides = {}) {
|
|
62
|
+
if (input.expectedHost) {
|
|
63
|
+
throw new RuntimeHostServiceManagerError('target_mismatch', 'An exact Host fence requires the canonical managed deployment operator');
|
|
64
|
+
}
|
|
62
65
|
const deps = runtimeHostServiceManagerDeps(overrides);
|
|
63
66
|
const configPath = resolveRuntimeHostManagedServiceConfigPath(input.clientDataRoot);
|
|
64
67
|
const configDirectory = dirname(configPath);
|
|
@@ -667,9 +670,6 @@ async function normalizeStateRoot(requestedRoot) {
|
|
|
667
670
|
throw new RuntimeHostServiceManagerError('invalid_config', `Invalid Runtime Host State Root: ${requestedRoot}`, { cause: error });
|
|
668
671
|
}
|
|
669
672
|
}
|
|
670
|
-
export async function resolveRuntimeHostManagedStateRoot(requestedRoot) {
|
|
671
|
-
return normalizeStateRoot(requestedRoot);
|
|
672
|
-
}
|
|
673
673
|
async function normalizeProjectDirectoryRoots(roots) {
|
|
674
674
|
let canonicalRoots;
|
|
675
675
|
try {
|
|
@@ -37,6 +37,7 @@ export class RuntimeHostSessionChannel {
|
|
|
37
37
|
#now;
|
|
38
38
|
#onTurnStarted;
|
|
39
39
|
#onRuntimeResourceChanged;
|
|
40
|
+
#onSessionDomainChanged;
|
|
40
41
|
#onInteractionPending;
|
|
41
42
|
#onInteractionResolved;
|
|
42
43
|
#onTranscriptSettlement;
|
|
@@ -71,6 +72,7 @@ export class RuntimeHostSessionChannel {
|
|
|
71
72
|
this.#now = options.now;
|
|
72
73
|
this.#onTurnStarted = options.onTurnStarted;
|
|
73
74
|
this.#onRuntimeResourceChanged = options.onRuntimeResourceChanged;
|
|
75
|
+
this.#onSessionDomainChanged = options.onSessionDomainChanged;
|
|
74
76
|
this.#onInteractionPending = options.onInteractionPending;
|
|
75
77
|
this.#onInteractionResolved = options.onInteractionResolved;
|
|
76
78
|
this.#onTranscriptSettlement = options.onTranscriptSettlement;
|
|
@@ -79,6 +81,16 @@ export class RuntimeHostSessionChannel {
|
|
|
79
81
|
this.#onSnapshotChanged = options.onSnapshotChanged;
|
|
80
82
|
this.#onFailed = options.onFailed;
|
|
81
83
|
this.#onRecovered = options.onRecovered;
|
|
84
|
+
this.#subscribeSessionDomainChanges(subscription);
|
|
85
|
+
}
|
|
86
|
+
#subscribeSessionDomainChanges(subscription) {
|
|
87
|
+
if (!this.#onSessionDomainChanged)
|
|
88
|
+
return;
|
|
89
|
+
subscription.subscribeSessionDomainChanges((frame) => {
|
|
90
|
+
if (!this.#closing && this.#subscription === subscription) {
|
|
91
|
+
this.#onSessionDomainChanged?.(frame);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
82
94
|
}
|
|
83
95
|
static async open(options) {
|
|
84
96
|
const subscription = await options.connection.openSessionSubscription({
|
|
@@ -343,6 +355,7 @@ export class RuntimeHostSessionChannel {
|
|
|
343
355
|
return;
|
|
344
356
|
}
|
|
345
357
|
this.#subscription = replacement;
|
|
358
|
+
this.#subscribeSessionDomainChanges(replacement);
|
|
346
359
|
this.#ready = false;
|
|
347
360
|
this.#pendingFrames.length = 0;
|
|
348
361
|
void this.#pump(replacement);
|