nx 23.3.0-beta.2 → 23.3.0-beta.3
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/bin/init-local.js +7 -1
- package/dist/src/command-line/add/add.js +5 -1
- package/dist/src/command-line/init/implementation/utils.js +6 -1
- package/dist/src/command-line/migrate/agentic/capture-generator-output.d.ts +25 -0
- package/dist/src/command-line/migrate/agentic/capture-generator-output.js +121 -5
- package/dist/src/command-line/migrate/agentic/close-agent-session.d.ts +21 -0
- package/dist/src/command-line/migrate/agentic/close-agent-session.js +126 -0
- package/dist/src/command-line/migrate/agentic/definitions.d.ts +24 -0
- package/dist/src/command-line/migrate/agentic/definitions.js +5 -2
- package/dist/src/command-line/migrate/agentic/handoff.d.ts +23 -0
- package/dist/src/command-line/migrate/agentic/handoff.js +62 -3
- package/dist/src/command-line/migrate/agentic/master/invocations.d.ts +12 -0
- package/dist/src/command-line/migrate/agentic/master/invocations.js +54 -0
- package/dist/src/command-line/migrate/agentic/master/run-master-session.d.ts +12 -0
- package/dist/src/command-line/migrate/agentic/master/run-master-session.js +99 -0
- package/dist/src/command-line/migrate/agentic/master/spawn-master.d.ts +31 -0
- package/dist/src/command-line/migrate/agentic/master/spawn-master.js +217 -0
- package/dist/src/command-line/migrate/agentic/prompts/fragments.js +3 -0
- package/dist/src/command-line/migrate/agentic/runner.d.ts +0 -21
- package/dist/src/command-line/migrate/agentic/runner.js +12 -249
- package/dist/src/command-line/migrate/agentic/terminal-repair.d.ts +1 -0
- package/dist/src/command-line/migrate/agentic/terminal-repair.js +27 -0
- package/dist/src/command-line/migrate/agentic/windows-cmd.d.ts +33 -0
- package/dist/src/command-line/migrate/agentic/windows-cmd.js +88 -0
- package/dist/src/command-line/migrate/deferred-output.d.ts +44 -0
- package/dist/src/command-line/migrate/deferred-output.js +92 -0
- package/dist/src/command-line/migrate/execute-migration.d.ts +9 -3
- package/dist/src/command-line/migrate/execute-migration.js +53 -16
- package/dist/src/command-line/migrate/migrate-commits.d.ts +4 -4
- package/dist/src/command-line/migrate/migrate-commits.js +7 -6
- package/dist/src/command-line/migrate/migrate.js +28 -13
- package/dist/src/command-line/migrate/run/broker.d.ts +138 -0
- package/dist/src/command-line/migrate/run/broker.js +507 -0
- package/dist/src/command-line/migrate/run/clean-retry.d.ts +12 -0
- package/dist/src/command-line/migrate/run/clean-retry.js +98 -0
- package/dist/src/command-line/migrate/run/index.d.ts +4 -3
- package/dist/src/command-line/migrate/run/index.js +7 -1
- package/dist/src/command-line/migrate/run/issues.d.ts +8 -1
- package/dist/src/command-line/migrate/run/issues.js +40 -6
- package/dist/src/command-line/migrate/run/orchestrator.d.ts +18 -1
- package/dist/src/command-line/migrate/run/orchestrator.js +409 -377
- package/dist/src/command-line/migrate/run/run-state.d.ts +16 -0
- package/dist/src/command-line/migrate/run/run-state.js +33 -2
- package/dist/src/command-line/migrate/run/runbook.js +8 -4
- package/dist/src/command-line/migrate/run/state-machine.d.ts +24 -1
- package/dist/src/command-line/migrate/run/state-machine.js +112 -21
- package/dist/src/command-line/migrate/run/util.d.ts +2 -1
- package/dist/src/command-line/migrate/run/util.js +3 -3
- package/dist/src/command-line/migrate/run/worker.js +170 -164
- package/dist/src/core/graph/main.js +1 -1
- package/dist/src/daemon/server/server.js +11 -8
- package/dist/src/native/nx.wasm32-wasi.debug.wasm +0 -0
- package/dist/src/native/nx.wasm32-wasi.wasm +0 -0
- package/dist/src/tasks-runner/run-command.js +3 -0
- package/dist/src/utils/git-utils.d.ts +12 -0
- package/dist/src/utils/git-utils.js +76 -30
- package/dist/src/utils/package-json.js +8 -0
- package/package.json +11 -11
|
@@ -20,6 +20,7 @@ exports.isAngularMigration = isAngularMigration;
|
|
|
20
20
|
const tslib_1 = require("tslib");
|
|
21
21
|
const pc = tslib_1.__importStar(require("picocolors"));
|
|
22
22
|
const child_process_1 = require("child_process");
|
|
23
|
+
const string_decoder_1 = require("string_decoder");
|
|
23
24
|
const path_1 = require("path");
|
|
24
25
|
const semver_1 = require("semver");
|
|
25
26
|
const handle_import_1 = require("../../utils/handle-import");
|
|
@@ -29,12 +30,12 @@ const logger_1 = require("../../utils/logger");
|
|
|
29
30
|
const text_1 = require("./text");
|
|
30
31
|
const package_json_1 = require("../../utils/package-json");
|
|
31
32
|
const package_manager_1 = require("../../utils/package-manager");
|
|
32
|
-
const output_1 = require("../../utils/output");
|
|
33
33
|
const fs_1 = require("fs");
|
|
34
34
|
const installation_directory_1 = require("../../utils/installation-directory");
|
|
35
35
|
const shell_quoting_1 = require("../../utils/shell-quoting");
|
|
36
36
|
const project_graph_1 = require("../../project-graph/project-graph");
|
|
37
37
|
const version_utils_1 = require("./version-utils");
|
|
38
|
+
const deferred_output_1 = require("./deferred-output");
|
|
38
39
|
function readPackageMigrationConfig(packageName, dir) {
|
|
39
40
|
const { path: packageJsonPath, packageJson: json } = (0, package_json_1.readModulePackageJson)(packageName, (0, installation_directory_1.getNxRequirePaths)(dir));
|
|
40
41
|
const config = (0, package_json_1.readNxMigrateConfig)(json);
|
|
@@ -61,32 +62,68 @@ function readPackageMigrationConfig(packageName, dir) {
|
|
|
61
62
|
};
|
|
62
63
|
}
|
|
63
64
|
}
|
|
64
|
-
|
|
65
|
+
/**
|
|
66
|
+
* With a `sink`, stdin is ignored and stdout and stderr are collected for a
|
|
67
|
+
* caller sharing the terminal. On POSIX the child stays in nx's process
|
|
68
|
+
* group. Without a sink, the install owns the terminal.
|
|
69
|
+
*/
|
|
70
|
+
function runInstall(nxWorkspaceRoot, phase = 'pre-migration', rerunCommand, sink) {
|
|
65
71
|
const cwd = nxWorkspaceRoot ?? process.cwd();
|
|
66
72
|
const packageManager = (0, package_manager_1.detectPackageManager)(cwd);
|
|
67
73
|
const pmCommands = (0, package_manager_1.getPackageManagerCommand)(packageManager, cwd);
|
|
74
|
+
const out = sink ?? deferred_output_1.terminalOutput;
|
|
68
75
|
const installCommand = `${pmCommands.install} ${pmCommands.ignoreScriptsFlag ?? ''}`;
|
|
69
|
-
|
|
76
|
+
out.notice('log', {
|
|
70
77
|
title: `Running '${installCommand}' to make sure necessary packages are installed`,
|
|
71
78
|
});
|
|
72
79
|
return new Promise((resolve, reject) => {
|
|
73
|
-
//
|
|
74
|
-
//
|
|
75
|
-
// stderr directly since
|
|
80
|
+
// npm's stderr is piped so peer dependency errors can be detected in it.
|
|
81
|
+
// Without a sink it is mirrored live and other package managers inherit
|
|
82
|
+
// stderr directly, since their output is not inspected.
|
|
76
83
|
const shouldCaptureStderr = packageManager === 'npm';
|
|
77
84
|
const child = (0, child_process_1.spawn)(installCommand, {
|
|
78
85
|
shell: true,
|
|
79
|
-
stdio:
|
|
86
|
+
stdio: sink
|
|
87
|
+
? ['ignore', 'pipe', 'pipe']
|
|
88
|
+
: ['inherit', 'inherit', shouldCaptureStderr ? 'pipe' : 'inherit'],
|
|
80
89
|
windowsHide: true,
|
|
81
90
|
cwd,
|
|
82
91
|
});
|
|
92
|
+
// Decoded per stream: a chunk boundary can split a multi-byte character,
|
|
93
|
+
// and a sequence still incomplete at the end is what `end()` returns.
|
|
94
|
+
const stdoutText = new string_decoder_1.StringDecoder('utf8');
|
|
95
|
+
const stderrText = new string_decoder_1.StringDecoder('utf8');
|
|
96
|
+
child.stdout?.on('data', (chunk) => out.raw(stdoutText.write(chunk)));
|
|
97
|
+
child.stdout?.on('end', () => out.raw(stdoutText.end()));
|
|
83
98
|
const stderrChunks = [];
|
|
84
99
|
child.stderr?.on('data', (chunk) => {
|
|
85
|
-
|
|
86
|
-
|
|
100
|
+
if (sink) {
|
|
101
|
+
out.raw(stderrText.write(chunk));
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
process.stderr.write(chunk);
|
|
105
|
+
}
|
|
106
|
+
if (shouldCaptureStderr)
|
|
107
|
+
stderrChunks.push(chunk);
|
|
108
|
+
});
|
|
109
|
+
if (sink)
|
|
110
|
+
child.stderr.on('end', () => out.raw(stderrText.end()));
|
|
111
|
+
// With a sink the rejection waits for `close`, which follows the streams'
|
|
112
|
+
// `end`: a caller must be able to render what it collected on rejection.
|
|
113
|
+
let spawnError = null;
|
|
114
|
+
child.on('error', (error) => {
|
|
115
|
+
if (sink) {
|
|
116
|
+
spawnError = error;
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
reject(error);
|
|
120
|
+
}
|
|
87
121
|
});
|
|
88
|
-
child.on('error', reject);
|
|
89
122
|
child.on('close', (code) => {
|
|
123
|
+
if (spawnError) {
|
|
124
|
+
reject(spawnError);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
90
127
|
if (code === 0) {
|
|
91
128
|
resolve();
|
|
92
129
|
return;
|
|
@@ -98,7 +135,7 @@ function runInstall(nxWorkspaceRoot, phase = 'pre-migration', rerunCommand) {
|
|
|
98
135
|
// (CLI migrate, `nx repair`, single-migration runner, etc.) surfaces
|
|
99
136
|
// it consistently. Top-level callers catch `NpmPeerDepsInstallError`
|
|
100
137
|
// and return a non-zero exit code without re-logging.
|
|
101
|
-
logNpmPeerDepsError(phase, rerunCommand);
|
|
138
|
+
logNpmPeerDepsError(phase, rerunCommand, out);
|
|
102
139
|
reject(new NpmPeerDepsInstallError());
|
|
103
140
|
return;
|
|
104
141
|
}
|
|
@@ -143,7 +180,7 @@ function formatSingleMigrationRerunCommand(migrationId) {
|
|
|
143
180
|
: migrationId;
|
|
144
181
|
return `nx migrate --run-migration=${id}`;
|
|
145
182
|
}
|
|
146
|
-
function logNpmPeerDepsError(phase, rerunCommand = 'nx migrate --run-migrations') {
|
|
183
|
+
function logNpmPeerDepsError(phase, rerunCommand = 'nx migrate --run-migrations', out = deferred_output_1.terminalOutput) {
|
|
147
184
|
const peerDepsResolutionSteps = [
|
|
148
185
|
'Recommended approaches (in order of preference):',
|
|
149
186
|
'',
|
|
@@ -160,7 +197,7 @@ function logNpmPeerDepsError(phase, rerunCommand = 'nx migrate --run-migrations'
|
|
|
160
197
|
` ${rerunCommand} --skip-install`,
|
|
161
198
|
];
|
|
162
199
|
if (phase === 'pre-migration') {
|
|
163
|
-
|
|
200
|
+
out.notice('error', {
|
|
164
201
|
title: 'You need to resolve the peer dependency conflicts before the migration can continue',
|
|
165
202
|
bodyLines: [
|
|
166
203
|
...peerDepsResolutionSteps,
|
|
@@ -173,7 +210,7 @@ function logNpmPeerDepsError(phase, rerunCommand = 'nx migrate --run-migrations'
|
|
|
173
210
|
});
|
|
174
211
|
}
|
|
175
212
|
else {
|
|
176
|
-
|
|
213
|
+
out.notice('error', {
|
|
177
214
|
title: 'Some migrations have been applied, but installing the updated dependencies failed',
|
|
178
215
|
bodyLines: [
|
|
179
216
|
...peerDepsResolutionSteps,
|
|
@@ -187,10 +224,10 @@ function logNpmPeerDepsError(phase, rerunCommand = 'nx migrate --run-migrations'
|
|
|
187
224
|
});
|
|
188
225
|
}
|
|
189
226
|
}
|
|
190
|
-
function logSkippedPostMigrationInstall(root) {
|
|
227
|
+
function logSkippedPostMigrationInstall(root, out = deferred_output_1.terminalOutput) {
|
|
191
228
|
const packageManager = (0, package_manager_1.detectPackageManager)(root);
|
|
192
229
|
const installCommand = (0, package_manager_1.getPackageManagerCommand)(packageManager, root).install;
|
|
193
|
-
|
|
230
|
+
out.notice('warn', {
|
|
194
231
|
title: 'Migrations updated your dependencies, but the install was skipped',
|
|
195
232
|
bodyLines: [`Run "${installCommand}" to install the updated dependencies.`],
|
|
196
233
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ResolvedAgentic } from './agentic/types';
|
|
2
|
+
import { type MigrateOutputSink } from './deferred-output';
|
|
2
3
|
/**
|
|
3
4
|
* Discriminated result for `commitMigrationIfRequested`. Distinguishes the
|
|
4
5
|
* shapes the executor needs to react to:
|
|
@@ -7,9 +8,8 @@ import type { ResolvedAgentic } from './agentic/types';
|
|
|
7
8
|
* HEAD` failed transiently — by contract the diff is no longer in the
|
|
8
9
|
* working tree.
|
|
9
10
|
* - `no-changes`: commits were requested but there was nothing to commit.
|
|
10
|
-
* - `failed`: the
|
|
11
|
-
*
|
|
12
|
-
* so the next successful commit can annotate its body.
|
|
11
|
+
* - `failed`: the attempt errored, possibly after the commit landed; tracked
|
|
12
|
+
* as pending so the next successful commit can annotate its body.
|
|
13
13
|
* - `disabled`: commits are off for this run.
|
|
14
14
|
*/
|
|
15
15
|
export type CommitResult = {
|
|
@@ -37,7 +37,7 @@ export declare function commitMigrationIfRequested(root: string, migration: {
|
|
|
37
37
|
}, shouldCreateCommits: boolean, commitPrefix: string, installDepsIfChanged: () => Promise<void>, pendingMigrations?: ReadonlyArray<{
|
|
38
38
|
package: string;
|
|
39
39
|
name: string;
|
|
40
|
-
}>, failureGuidance?: string): Promise<CommitResult>;
|
|
40
|
+
}>, failureGuidance?: string, out?: MigrateOutputSink): Promise<CommitResult>;
|
|
41
41
|
/**
|
|
42
42
|
* Commits any pre-existing working-tree state into a dedicated "checkpoint"
|
|
43
43
|
* commit before the first migration runs. Without this, the first migration's
|
|
@@ -15,6 +15,7 @@ const logger_1 = require("../../utils/logger");
|
|
|
15
15
|
const output_1 = require("../../utils/output");
|
|
16
16
|
const types_1 = require("./agentic/types");
|
|
17
17
|
const safe_prompt_1 = require("./safe-prompt");
|
|
18
|
+
const deferred_output_1 = require("./deferred-output");
|
|
18
19
|
// `git add -A` captures an orchestrated run's scratch state whenever the
|
|
19
20
|
// ignore rule that normally hides it goes missing mid-run (a checkout, a
|
|
20
21
|
// .gitignore edit, or the migration's own changes).
|
|
@@ -28,7 +29,7 @@ const MIGRATE_COMMIT_EXCLUDES = [types_1.MIGRATE_RUNS_RELATIVE_DIR];
|
|
|
28
29
|
* behavior; a caller with no later commit or recap to absorb the diff (the
|
|
29
30
|
* standalone single-migration worker) passes its own.
|
|
30
31
|
*/
|
|
31
|
-
async function commitMigrationIfRequested(root, migration, shouldCreateCommits, commitPrefix, installDepsIfChanged, pendingMigrations = [], failureGuidance = '
|
|
32
|
+
async function commitMigrationIfRequested(root, migration, shouldCreateCommits, commitPrefix, installDepsIfChanged, pendingMigrations = [], failureGuidance = 'Any uncommitted changes will be included in the next successful commit, which will reference this migration; if they remain uncommitted, the end-of-run output will list this migration so you can commit or revert them manually.', out = deferred_output_1.terminalOutput) {
|
|
32
33
|
if (!shouldCreateCommits)
|
|
33
34
|
return { status: 'disabled' };
|
|
34
35
|
await installDepsIfChanged();
|
|
@@ -36,22 +37,22 @@ async function commitMigrationIfRequested(root, migration, shouldCreateCommits,
|
|
|
36
37
|
// dir, or the prompt half made no change: log neutrally, not as an error.
|
|
37
38
|
// The probe excludes what the commit excludes, else the commit fails empty.
|
|
38
39
|
if (!(0, git_utils_1.hasUncommittedChanges)(root, MIGRATE_COMMIT_EXCLUDES)) {
|
|
39
|
-
|
|
40
|
+
out.line('dim', `- No changes to commit for ${migration.name}.`);
|
|
40
41
|
return { status: 'no-changes' };
|
|
41
42
|
}
|
|
42
43
|
const commitMessage = buildCommitMessage(`${commitPrefix}${migration.name}`, pendingMigrations);
|
|
43
44
|
try {
|
|
44
|
-
const sha = (0, git_utils_1.
|
|
45
|
+
const sha = await (0, git_utils_1.tryCommitChangesAsync)(commitMessage, root, MIGRATE_COMMIT_EXCLUDES);
|
|
45
46
|
if (sha)
|
|
46
47
|
return { status: 'committed', sha };
|
|
47
48
|
// null = commit landed but `git rev-parse HEAD` failed (see
|
|
48
|
-
// `
|
|
49
|
-
|
|
49
|
+
// `tryCommitChangesAsync`). Degraded-but-correct — log yellow, not red.
|
|
50
|
+
out.line('yellow', `The commit for ${migration.name} was created, but its sha could not be resolved (\`git rev-parse HEAD\` failed transiently). Continuing without recording the sha for this step.`);
|
|
50
51
|
return { status: 'committed', sha: null };
|
|
51
52
|
}
|
|
52
53
|
catch (err) {
|
|
53
54
|
const reason = err instanceof Error ? err.message : String(err);
|
|
54
|
-
|
|
55
|
+
out.line('red', `The commit for ${migration.name} failed:\n${reason}\nCheck \`git status\` and \`git log\`; the commit may have landed despite this error. ${failureGuidance}`);
|
|
55
56
|
return { status: 'failed', reason };
|
|
56
57
|
}
|
|
57
58
|
}
|
|
@@ -30,6 +30,7 @@ const tar_1 = require("../../utils/tar");
|
|
|
30
30
|
const write_formatted_json_file_1 = require("../../utils/write-formatted-json-file");
|
|
31
31
|
const shell_quoting_1 = require("../../utils/shell-quoting");
|
|
32
32
|
const logger_1 = require("../../utils/logger");
|
|
33
|
+
const native_1 = require("../../native");
|
|
33
34
|
const git_utils_1 = require("../../utils/git-utils");
|
|
34
35
|
const package_json_1 = require("../../utils/package-json");
|
|
35
36
|
const package_manager_1 = require("../../utils/package-manager");
|
|
@@ -2126,6 +2127,20 @@ async function runMigrations(root, opts, args, isVerbose, shouldCreateCommits, c
|
|
|
2126
2127
|
}
|
|
2127
2128
|
const migrationsJson = (0, fileutils_1.readJsonFile)((0, path_1.join)(root, opts.runMigrations));
|
|
2128
2129
|
const migrations = migrationsJson.migrations;
|
|
2130
|
+
// Defer the nx package lookup until an orchestrated branch needs this payload.
|
|
2131
|
+
const orchestratorInitInput = (createCommits) => ({
|
|
2132
|
+
root,
|
|
2133
|
+
migrationsJson,
|
|
2134
|
+
createCommits,
|
|
2135
|
+
commitPrefix,
|
|
2136
|
+
// The flag only, never NX_MIGRATE_SKIP_INSTALL: the wrapper's local
|
|
2137
|
+
// re-exec sets that env var for its own hop, and it says nothing about
|
|
2138
|
+
// what the user asked for.
|
|
2139
|
+
skipInstall: shouldSkipInstall,
|
|
2140
|
+
installedNxVersion: (0, package_json_1.readModulePackageJson)('nx', (0, installation_directory_1.getNxRequirePaths)(root))
|
|
2141
|
+
.packageJson.version,
|
|
2142
|
+
validate: opts.validate,
|
|
2143
|
+
});
|
|
2129
2144
|
// An outer agent drives the loop, so hand off to the orchestrator instead of
|
|
2130
2145
|
// the classic loop: init either starts a fresh run or resumes an already-
|
|
2131
2146
|
// active one. `--run-id` reconciles are dispatched separately and never
|
|
@@ -2164,20 +2179,10 @@ async function runMigrations(root, opts, args, isVerbose, shouldCreateCommits, c
|
|
|
2164
2179
|
return;
|
|
2165
2180
|
}
|
|
2166
2181
|
}
|
|
2167
|
-
const
|
|
2182
|
+
const init = orchestratorInitInput(effectiveCreateCommits);
|
|
2168
2183
|
const { runOrchestratorInit } = require('./run');
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
migrationsJson,
|
|
2172
|
-
createCommits: effectiveCreateCommits,
|
|
2173
|
-
commitPrefix,
|
|
2174
|
-
// The flag only, never NX_MIGRATE_SKIP_INSTALL: the wrapper's local
|
|
2175
|
-
// re-exec sets that env var for its own hop, and it says nothing about
|
|
2176
|
-
// what the user asked for.
|
|
2177
|
-
skipInstall: shouldSkipInstall,
|
|
2178
|
-
installedNxVersion: orchestratorNxPackageJson.version,
|
|
2179
|
-
validate: opts.validate,
|
|
2180
|
-
});
|
|
2184
|
+
await runOrchestratorInit(init);
|
|
2185
|
+
return;
|
|
2181
2186
|
}
|
|
2182
2187
|
(0, migrate_analytics_1.reportMigrateRunStart)({
|
|
2183
2188
|
createCommits: shouldCreateCommits ?? false,
|
|
@@ -2213,6 +2218,16 @@ async function runMigrations(root, opts, args, isVerbose, shouldCreateCommits, c
|
|
|
2213
2218
|
!(await (0, migrate_commits_1.confirmMigrationCommitsOnDefaultBranch)(root, 'running migrations'))) {
|
|
2214
2219
|
return;
|
|
2215
2220
|
}
|
|
2221
|
+
// Dark: with the env var set, the agent drives the whole run through the
|
|
2222
|
+
// orchestrator from one session instead of being spawned per step. Not
|
|
2223
|
+
// under WASM, where the broker has no native lock to detect a dead parent.
|
|
2224
|
+
if (agentic.kind === 'enabled' &&
|
|
2225
|
+
process.env.NX_MIGRATE_ORCHESTRATOR === 'true' &&
|
|
2226
|
+
!native_1.IS_WASM) {
|
|
2227
|
+
const init = orchestratorInitInput(effectiveCreateCommits);
|
|
2228
|
+
const { runMasterSession } = require('./agentic/master/run-master-session');
|
|
2229
|
+
return await runMasterSession({ ...init, agent: agentic.selectedAgent });
|
|
2230
|
+
}
|
|
2216
2231
|
const shouldRunValidation = resolveShouldRunValidation({
|
|
2217
2232
|
validate: opts.validate,
|
|
2218
2233
|
agenticKind: agentic.kind,
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { type DeferredOutputRecord } from '../deferred-output';
|
|
2
|
+
import { type CommitResult } from '../migrate-commits';
|
|
3
|
+
import { type MigrateRunPolicy, type MigrateRunState, type MigrateStep, type MigrateTreeOperation } from './run-state';
|
|
4
|
+
export declare const BROKER_ENV_VAR = "NX_MIGRATE_BROKER";
|
|
5
|
+
export type BrokerRequestKind = 'commit' | 'install' | 'fold-install' | 'action-install' | 'reset';
|
|
6
|
+
export type InstallSeam = 'install' | 'fold-install' | 'action-install';
|
|
7
|
+
export interface BrokerRequest {
|
|
8
|
+
kind: BrokerRequestKind;
|
|
9
|
+
stepId: string;
|
|
10
|
+
attempt: number;
|
|
11
|
+
invocation?: string;
|
|
12
|
+
}
|
|
13
|
+
export type BrokerResult = {
|
|
14
|
+
kind: 'commit';
|
|
15
|
+
result: CommitResult;
|
|
16
|
+
absorbedStepIds: string[];
|
|
17
|
+
output: DeferredOutputRecord[];
|
|
18
|
+
} | {
|
|
19
|
+
kind: 'installed';
|
|
20
|
+
output: DeferredOutputRecord[];
|
|
21
|
+
} | {
|
|
22
|
+
kind: 'install-failed';
|
|
23
|
+
message: string;
|
|
24
|
+
peerDeps: boolean;
|
|
25
|
+
output: DeferredOutputRecord[];
|
|
26
|
+
} | {
|
|
27
|
+
kind: 'reset';
|
|
28
|
+
error?: string;
|
|
29
|
+
} | {
|
|
30
|
+
kind: 'stale';
|
|
31
|
+
};
|
|
32
|
+
export interface BrokeredCommit {
|
|
33
|
+
result: CommitResult;
|
|
34
|
+
absorbedStepIds: string[];
|
|
35
|
+
/**
|
|
36
|
+
* True when the session's parent ran the commit and recorded whatever it
|
|
37
|
+
* produced before answering, so the caller appends nothing. False for an
|
|
38
|
+
* in-process commit, which the caller records.
|
|
39
|
+
*/
|
|
40
|
+
recorded: boolean;
|
|
41
|
+
}
|
|
42
|
+
/** The request no longer matches the step: another attempt owns it. */
|
|
43
|
+
export declare class BrokerStaleRequestError extends Error {
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The advertised parent could not accept the request, or went away before
|
|
47
|
+
* answering it. It may have landed the install or the commit; the tree and
|
|
48
|
+
* the run state say.
|
|
49
|
+
*/
|
|
50
|
+
export declare class BrokerUnavailableError extends Error {
|
|
51
|
+
}
|
|
52
|
+
/** Another live process holds the working tree for an operation of its own. */
|
|
53
|
+
export declare class TreeBusyError extends Error {
|
|
54
|
+
}
|
|
55
|
+
export interface TreeOperationRequest {
|
|
56
|
+
kind: BrokerRequestKind | 'checkpoint';
|
|
57
|
+
stepId?: string;
|
|
58
|
+
attempt?: number;
|
|
59
|
+
}
|
|
60
|
+
export interface TreeLease {
|
|
61
|
+
readonly owner: string;
|
|
62
|
+
markedStepId?: string;
|
|
63
|
+
release(): void;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Owned by the scope that runs an operation and its state write: a seam that
|
|
67
|
+
* acquires in process writes the lease here before its fallible callback
|
|
68
|
+
* runs, and the scope releases it in a `finally` once the write landed or
|
|
69
|
+
* failed. A brokered call leaves it empty; the parent holds its own.
|
|
70
|
+
*/
|
|
71
|
+
export interface TreeScope {
|
|
72
|
+
lease?: TreeLease;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Reserves the working tree for one operation in a single fresh-state write:
|
|
76
|
+
* the request must still be at its seam (else the attempt moved on and the
|
|
77
|
+
* request is stale), and no other live process may hold a reservation.
|
|
78
|
+
* A reservation whose owner process is gone holds nothing.
|
|
79
|
+
*/
|
|
80
|
+
export declare function acquireTreeOperation(dir: string, request: TreeOperationRequest, owner?: string): TreeLease;
|
|
81
|
+
/**
|
|
82
|
+
* Owner-checked: a lease released late never drops a newer reservation, nor
|
|
83
|
+
* the mark it set.
|
|
84
|
+
*/
|
|
85
|
+
export declare function releaseTreeOperation(dir: string, owner: string, markedStepId?: string): void;
|
|
86
|
+
/** The reservation a live process other than `owner` holds, if any. */
|
|
87
|
+
export declare function liveTreeOperation(state: MigrateRunState, owner?: string): MigrateTreeOperation | undefined;
|
|
88
|
+
export declare function treeBusyMessage(held: MigrateTreeOperation): string;
|
|
89
|
+
export declare function treeOperationLabel(held: Pick<MigrateTreeOperation, 'kind' | 'stepId'>): string;
|
|
90
|
+
export declare function brokerDir(runDirPath: string): string;
|
|
91
|
+
/**
|
|
92
|
+
* Runs the step's install and commit where they can land: in this process
|
|
93
|
+
* unless a parent session advertised its broker, in which case the request
|
|
94
|
+
* goes to the parent and the answer comes back with the output the parent
|
|
95
|
+
* collected, printed here. The absorbed step ids come from whichever side
|
|
96
|
+
* ran the commit, so the ledger entry names what its `git add -A` took.
|
|
97
|
+
*/
|
|
98
|
+
export declare function commitStepTree(dir: string, step: MigrateStep, absorbedStepIds: string[], commitInProcess: () => Promise<CommitResult>, scope: TreeScope): Promise<BrokeredCommit>;
|
|
99
|
+
/**
|
|
100
|
+
* The same for a step that owes only its install: no commit is due, or the
|
|
101
|
+
* commit waits for a fold.
|
|
102
|
+
*/
|
|
103
|
+
export declare function installStepTree(dir: string, step: MigrateStep, seam: InstallSeam, installInProcess: () => Promise<void>, scope: TreeScope): Promise<void>;
|
|
104
|
+
/**
|
|
105
|
+
* The same for the reset a clean retry of a failed or died step needs. Runs
|
|
106
|
+
* `resetInProcess` under the reservation, or asks the parent, which resets
|
|
107
|
+
* against the state it reads then; a reset that could not run throws.
|
|
108
|
+
*/
|
|
109
|
+
export declare function resetStepTree(dir: string, step: MigrateStep, resetInProcess: () => void, scope: TreeScope): Promise<void>;
|
|
110
|
+
/**
|
|
111
|
+
* The parent side. Holds one exclusive lock for the session's lifetime so a
|
|
112
|
+
* waiting step can tell a slow parent from a dead one, answers each request
|
|
113
|
+
* once, and removes its own requests on close; its answers stay for the steps
|
|
114
|
+
* still reading them. Requests carrying another session's nonce belong to
|
|
115
|
+
* that session and are never touched. Whether to install or commit comes from
|
|
116
|
+
* the policy the session started with, never from run state, which the
|
|
117
|
+
* agent's sandbox can write.
|
|
118
|
+
*/
|
|
119
|
+
export declare class MigrateCommitBroker {
|
|
120
|
+
private readonly root;
|
|
121
|
+
private readonly dir;
|
|
122
|
+
private readonly reconcileCommand;
|
|
123
|
+
private readonly policy;
|
|
124
|
+
readonly nonce: string;
|
|
125
|
+
private readonly handled;
|
|
126
|
+
private inFlight;
|
|
127
|
+
private readonly lock;
|
|
128
|
+
constructor(root: string, dir: string, reconcileCommand: string, policy: MigrateRunPolicy);
|
|
129
|
+
/** The request whose operation this process is running right now. */
|
|
130
|
+
get requestInFlight(): BrokerRequest | null;
|
|
131
|
+
/** Answers this session's unanswered requests, one at a time. */
|
|
132
|
+
service(): Promise<void>;
|
|
133
|
+
private record;
|
|
134
|
+
private answer;
|
|
135
|
+
/** Releases the lock; call after the last `service` settled. */
|
|
136
|
+
close(): void;
|
|
137
|
+
private notADirectory;
|
|
138
|
+
}
|