nx 23.2.1 → 23.3.0-beta.1
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/src/command-line/format/format.js +2 -18
- package/dist/src/command-line/graph/graph.js +42 -3
- package/dist/src/command-line/init/implementation/format.js +2 -3
- package/dist/src/command-line/migrate/agentic/definitions.js +75 -19
- package/dist/src/command-line/migrate/agentic/format-command.d.ts +14 -0
- package/dist/src/command-line/migrate/agentic/format-command.js +44 -0
- package/dist/src/command-line/migrate/agentic/handoff-gitignore.d.ts +3 -3
- package/dist/src/command-line/migrate/agentic/handoff-gitignore.js +3 -3
- package/dist/src/command-line/migrate/agentic/handoff.d.ts +27 -17
- package/dist/src/command-line/migrate/agentic/handoff.js +107 -24
- package/dist/src/command-line/migrate/agentic/instruction-files.d.ts +21 -0
- package/dist/src/command-line/migrate/agentic/instruction-files.js +36 -0
- package/dist/src/command-line/migrate/agentic/prompts/fragments.d.ts +16 -0
- package/dist/src/command-line/migrate/agentic/prompts/fragments.js +70 -0
- package/dist/src/command-line/migrate/agentic/prompts/generic-validation.js +4 -3
- package/dist/src/command-line/migrate/agentic/prompts/system-prompt.d.ts +25 -1
- package/dist/src/command-line/migrate/agentic/prompts/system-prompt.js +57 -34
- package/dist/src/command-line/migrate/agentic/run-step.js +28 -4
- package/dist/src/command-line/migrate/agentic/runner.d.ts +17 -8
- package/dist/src/command-line/migrate/agentic/runner.js +110 -55
- package/dist/src/command-line/migrate/agentic/types.d.ts +23 -8
- package/dist/src/command-line/migrate/agentic/types.js +10 -7
- package/dist/src/command-line/migrate/migrate-commits.d.ts +1 -0
- package/dist/src/command-line/migrate/migrate-commits.js +9 -0
- package/dist/src/command-line/migrate/migrate-output.d.ts +3 -7
- package/dist/src/command-line/migrate/migrate-output.js +3 -7
- package/dist/src/command-line/migrate/migrate.js +21 -8
- package/dist/src/command-line/migrate/run/agent-output.d.ts +10 -0
- package/dist/src/command-line/migrate/run/agent-output.js +16 -2
- package/dist/src/command-line/migrate/run/agent-work-payload.d.ts +37 -0
- package/dist/src/command-line/migrate/run/agent-work-payload.js +142 -0
- package/dist/src/command-line/migrate/run/atomic-write.d.ts +8 -0
- package/dist/src/command-line/migrate/run/atomic-write.js +17 -0
- package/dist/src/command-line/migrate/run/index.d.ts +1 -1
- package/dist/src/command-line/migrate/run/issues.d.ts +106 -0
- package/dist/src/command-line/migrate/run/issues.js +980 -0
- package/dist/src/command-line/migrate/run/orchestrator.d.ts +1 -0
- package/dist/src/command-line/migrate/run/orchestrator.js +662 -105
- package/dist/src/command-line/migrate/run/run-state.d.ts +43 -8
- package/dist/src/command-line/migrate/run/run-state.js +184 -20
- package/dist/src/command-line/migrate/run/runbook.d.ts +17 -0
- package/dist/src/command-line/migrate/run/runbook.js +97 -0
- package/dist/src/command-line/migrate/run/state-lock.d.ts +2 -2
- package/dist/src/command-line/migrate/run/state-lock.js +2 -2
- package/dist/src/command-line/migrate/run/state-machine.d.ts +5 -1
- package/dist/src/command-line/migrate/run/state-machine.js +27 -1
- package/dist/src/command-line/migrate/run/worker.js +206 -56
- package/dist/src/command-line/nx-cloud/connect/connect-to-nx-cloud.js +5 -1
- package/dist/src/command-line/release/publish.js +11 -4
- package/dist/src/command-line/release/utils/remote-release-clients/github.js +3 -3
- package/dist/src/command-line/release/utils/remote-release-clients/gitlab.js +3 -3
- package/dist/src/command-line/release/utils/remote-release-clients/remote-release-client.d.ts +2 -0
- package/dist/src/command-line/release/utils/remote-release-clients/remote-release-client.js +35 -0
- package/dist/src/command-line/yargs-utils/shared-options.d.ts +1 -1
- package/dist/src/command-line/yargs-utils/shared-options.js +22 -4
- package/dist/src/config/workspace-json-project-json.d.ts +2 -0
- package/dist/src/core/graph/main.js +1 -1
- package/dist/src/daemon/client/client.d.ts +4 -2
- package/dist/src/daemon/client/client.js +20 -11
- package/dist/src/daemon/message-types/run-tasks-execution-hooks.d.ts +3 -2
- package/dist/src/daemon/server/handle-hash-tasks.d.ts +8 -2
- package/dist/src/daemon/server/handle-hash-tasks.js +16 -3
- package/dist/src/daemon/server/handle-outputs-changes.d.ts +2 -2
- package/dist/src/daemon/server/handle-outputs-changes.js +21 -6
- package/dist/src/daemon/server/handle-tasks-execution-hooks.d.ts +3 -2
- package/dist/src/daemon/server/outputs-tracking.d.ts +6 -0
- package/dist/src/daemon/server/outputs-tracking.js +18 -0
- package/dist/src/daemon/server/project-graph-incremental-recomputation.d.ts +22 -7
- package/dist/src/daemon/server/project-graph-incremental-recomputation.js +99 -69
- package/dist/src/daemon/server/restart-checks.d.ts +22 -0
- package/dist/src/daemon/server/restart-checks.js +77 -0
- package/dist/src/daemon/server/server.js +28 -18
- package/dist/src/daemon/server/shutdown-utils.d.ts +0 -5
- package/dist/src/daemon/server/shutdown-utils.js +3 -26
- package/dist/src/hasher/hash-task.js +20 -16
- package/dist/src/hasher/native-task-hasher-impl.d.ts +8 -0
- package/dist/src/hasher/native-task-hasher-impl.js +46 -3
- package/dist/src/hasher/task-hasher.d.ts +9 -0
- package/dist/src/hasher/task-hasher.js +14 -0
- package/dist/src/native/index.d.ts +202 -21
- package/dist/src/native/index.js +3 -3
- package/dist/src/native/native-bindings.js +3 -1
- package/dist/src/native/nx.wasm32-wasi.debug.wasm +0 -0
- package/dist/src/native/nx.wasm32-wasi.wasm +0 -0
- package/dist/src/plugins/js/lock-file/pnpm-parser.js +1 -1
- package/dist/src/plugins/js/lock-file/utils/pnpm-normalizer.d.ts +2 -0
- package/dist/src/plugins/js/lock-file/utils/pnpm-normalizer.js +22 -0
- package/dist/src/plugins/js/utils/register.js +2 -1
- package/dist/src/project-graph/plugins/isolation/isolated-plugin.js +2 -1
- package/dist/src/project-graph/plugins/isolation/messaging.d.ts +3 -2
- package/dist/src/project-graph/plugins/isolation/plugin-worker.js +5 -1
- package/dist/src/project-graph/plugins/loaded-nx-plugin.d.ts +3 -2
- package/dist/src/project-graph/plugins/loaded-nx-plugin.js +5 -1
- package/dist/src/project-graph/plugins/task-results-stub.d.ts +50 -0
- package/dist/src/project-graph/plugins/task-results-stub.js +111 -0
- package/dist/src/project-graph/plugins/tasks-execution-hooks.d.ts +3 -2
- package/dist/src/project-graph/plugins/tasks-execution-hooks.js +2 -1
- package/dist/src/project-graph/project-graph.js +2 -0
- package/dist/src/tasks-runner/batch/run-batch.js +15 -0
- package/dist/src/tasks-runner/cache.d.ts +35 -0
- package/dist/src/tasks-runner/cache.js +40 -0
- package/dist/src/tasks-runner/create-task-graph.js +1 -0
- package/dist/src/tasks-runner/default-tasks-runner.js +1 -1
- package/dist/src/tasks-runner/forked-process-task-runner.d.ts +1 -1
- package/dist/src/tasks-runner/forked-process-task-runner.js +8 -7
- package/dist/src/tasks-runner/init-tasks-runner.js +5 -1
- package/dist/src/tasks-runner/is-tui-enabled.js +4 -3
- package/dist/src/tasks-runner/life-cycle.d.ts +7 -0
- package/dist/src/tasks-runner/life-cycle.js +7 -0
- package/dist/src/tasks-runner/life-cycles/summary-terminal-output-life-cycle.d.ts +35 -0
- package/dist/src/tasks-runner/life-cycles/summary-terminal-output-life-cycle.js +146 -0
- package/dist/src/tasks-runner/prune-task-graph.d.ts +17 -0
- package/dist/src/tasks-runner/prune-task-graph.js +45 -0
- package/dist/src/tasks-runner/run-command.js +14 -3
- package/dist/src/tasks-runner/running-tasks/batch-process.d.ts +93 -19
- package/dist/src/tasks-runner/running-tasks/batch-process.js +184 -88
- package/dist/src/tasks-runner/task-orchestrator.d.ts +64 -2
- package/dist/src/tasks-runner/task-orchestrator.js +232 -46
- package/dist/src/tasks-runner/terminal-output-path.d.ts +11 -0
- package/dist/src/tasks-runner/terminal-output-path.js +18 -0
- package/dist/src/tasks-runner/utils.js +5 -2
- package/dist/src/utils/ab-testing.d.ts +3 -0
- package/dist/src/utils/ab-testing.js +11 -3
- package/dist/src/utils/chunkify.d.ts +1 -7
- package/dist/src/utils/chunkify.js +2 -8
- package/dist/src/utils/command-line-utils.d.ts +19 -0
- package/dist/src/utils/command-line-utils.js +10 -3
- package/dist/src/utils/formatters/index.d.ts +6 -0
- package/dist/src/utils/formatters/index.js +5 -0
- package/dist/src/utils/formatters/prettier.d.ts +0 -8
- package/dist/src/utils/formatters/prettier.js +4 -26
- package/dist/src/utils/installed-nx-version.js +2 -48
- package/dist/src/utils/min-release-age/packument.js +1 -1
- package/dist/src/utils/output.d.ts +22 -3
- package/dist/src/utils/output.js +28 -5
- package/dist/src/utils/package-json.d.ts +25 -0
- package/dist/src/utils/package-json.js +41 -6
- package/dist/src/utils/package-manager.d.ts +6 -0
- package/dist/src/utils/package-manager.js +10 -0
- package/dist/src/utils/provenance.js +20 -4
- package/dist/src/utils/workspace-context.d.ts +47 -3
- package/dist/src/utils/workspace-context.js +162 -10
- package/package.json +12 -12
- package/schemas/nx-schema.json +4 -0
- package/schemas/project-schema.json +4 -0
- package/dist/src/daemon/server/file-watching/route-workspace-changes.d.ts +0 -9
- package/dist/src/daemon/server/file-watching/route-workspace-changes.js +0 -76
- package/dist/src/daemon/server/watcher.d.ts +0 -18
- package/dist/src/daemon/server/watcher.js +0 -126
|
@@ -7,7 +7,6 @@ const git_utils_1 = require("../../../utils/git-utils");
|
|
|
7
7
|
const fileutils_1 = require("../../../utils/fileutils");
|
|
8
8
|
const installation_directory_1 = require("../../../utils/installation-directory");
|
|
9
9
|
const package_json_1 = require("../../../utils/package-json");
|
|
10
|
-
const inception_1 = require("../agentic/inception");
|
|
11
10
|
const print_dropped_agent_context_1 = require("../agentic/print-dropped-agent-context");
|
|
12
11
|
const select_1 = require("../agentic/select");
|
|
13
12
|
const types_1 = require("../agentic/types");
|
|
@@ -25,6 +24,8 @@ const state_lock_1 = require("./state-lock");
|
|
|
25
24
|
const util_1 = require("./util");
|
|
26
25
|
const text_1 = require("../text");
|
|
27
26
|
const agent_output_1 = require("./agent-output");
|
|
27
|
+
const agent_work_payload_1 = require("./agent-work-payload");
|
|
28
|
+
const issues_1 = require("./issues");
|
|
28
29
|
async function runSingleMigrationWorker(input) {
|
|
29
30
|
const { root, runMigration, runId, commitPrefix, interactive, skipInstall, isVerbose, } = input;
|
|
30
31
|
// The worker is a second CLI entry point: the run id reaches runDir() (where
|
|
@@ -317,22 +318,24 @@ async function runStandalone(root, migration, opts) {
|
|
|
317
318
|
// and no "apply this manually" block for the user either.
|
|
318
319
|
if ((0, migration_shape_1.isHybridMigration)(migration) && !skipAgentic) {
|
|
319
320
|
emitOrPrintPrompt(root, migration, agentic.kind, {
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
321
|
+
impl: {
|
|
322
|
+
logs,
|
|
323
|
+
changes,
|
|
324
|
+
agentContext,
|
|
325
|
+
},
|
|
326
|
+
resolvedCollection,
|
|
327
|
+
});
|
|
324
328
|
}
|
|
325
329
|
}
|
|
326
330
|
async function runRecorded(root, runId, migration, skipInstall, isVerbose) {
|
|
327
331
|
const dir = (0, run_state_1.runDir)(root, runId);
|
|
328
332
|
// Version refusal (NewerRunStateFormatError) propagates.
|
|
329
333
|
let state = (0, run_state_1.readRunState)(dir);
|
|
330
|
-
// A recorded run never resolves the agentic flow
|
|
331
|
-
//
|
|
332
|
-
// the
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
: 'disabled';
|
|
334
|
+
// A recorded run never resolves the agentic flow: the outer agent drives it,
|
|
335
|
+
// and the dispensed command is part of the orchestrated protocol whoever
|
|
336
|
+
// re-runs it. Gating the block on ambient agent detection would park the
|
|
337
|
+
// step awaiting an outcome whose payload was never emitted.
|
|
338
|
+
const agenticKind = 'inside-agent';
|
|
336
339
|
const migrationId = `${migration.package}:${migration.name}`;
|
|
337
340
|
// The plan was read from the latest round's snapshot, so only that round's
|
|
338
341
|
// step may match; a same-id step from an older round must not.
|
|
@@ -350,24 +353,71 @@ async function runRecorded(root, runId, migration, skipInstall, isVerbose) {
|
|
|
350
353
|
startedAt: (0, util_1.nowIso)(),
|
|
351
354
|
});
|
|
352
355
|
// A prior attempt's generator half already ran, so this attempt must not
|
|
353
|
-
// reapply it against a tree that already holds its changes
|
|
354
|
-
//
|
|
355
|
-
//
|
|
356
|
-
// Read from the state the start transition returned: a delayed invocation
|
|
357
|
-
// may have claimed a later attempt whose flag its entry snapshot predates.
|
|
356
|
+
// reapply it against a tree that already holds its changes. Read from the
|
|
357
|
+
// state the start transition returned, not the entry snapshot: a delayed
|
|
358
|
+
// invocation may have claimed a later attempt whose flag `step` predates.
|
|
358
359
|
const startedStep = state.steps.find((s) => s.id === step.id);
|
|
359
360
|
const generatorAlreadyCompleted = startedStep.generatorCompleted === true;
|
|
360
361
|
// The run records its own install policy because dispensed worker commands
|
|
361
362
|
// are re-invoked by the loop and never carry the user's flags; an explicit
|
|
362
363
|
// --skip-install on this invocation still applies on top of it.
|
|
363
364
|
const effectiveSkipInstall = state.skipInstall === true || skipInstall;
|
|
365
|
+
// The run records the resolved validation policy at init; absent (a state
|
|
366
|
+
// predating the field) falls back to the same default init applies.
|
|
367
|
+
const shouldValidate = state.validate !== false;
|
|
368
|
+
// Called before a retry hands the step's work back: the prompt or validation
|
|
369
|
+
// may need the dependencies the earlier attempt's generator added.
|
|
370
|
+
const reinstallFromBaseline = () => recordingInstallFailure(dir, step.id, () => (0, util_1.installDepsChangedSinceDispense)(root, dir, startedStep, effectiveSkipInstall, `${(0, execute_migration_1.formatSingleMigrationRerunCommand)(migrationId)} --run-id=${runId}`));
|
|
364
371
|
let outcome;
|
|
372
|
+
let awaitingKind;
|
|
373
|
+
const payloadPath = (0, agent_work_payload_1.agentWorkPayloadPath)(dir, step.id, startedStep.attempt);
|
|
365
374
|
try {
|
|
366
|
-
if ((0, migration_shape_1.isPromptOnlyMigration)(migration)
|
|
367
|
-
(
|
|
368
|
-
|
|
375
|
+
if ((0, migration_shape_1.isPromptOnlyMigration)(migration)) {
|
|
376
|
+
emitOrPrintPrompt(root, migration, agenticKind, {
|
|
377
|
+
persistPath: payloadPath,
|
|
378
|
+
});
|
|
379
|
+
awaitingKind = 'migration-prompt';
|
|
380
|
+
}
|
|
381
|
+
else if (generatorAlreadyCompleted &&
|
|
382
|
+
startedStep.agenticWaived !== true &&
|
|
383
|
+
(0, migration_shape_1.isHybridMigration)(migration)) {
|
|
384
|
+
await reinstallFromBaseline();
|
|
385
|
+
const carried = (0, agent_work_payload_1.latestStoredAgentWorkPayload)(dir, step.id, startedStep.attempt, startedStep.generatorCompletedAtAttempt, {
|
|
386
|
+
migrationId,
|
|
387
|
+
kind: 'migration-prompt',
|
|
388
|
+
promptPath: migration.prompt,
|
|
389
|
+
});
|
|
390
|
+
if (carried) {
|
|
391
|
+
reemitCarriedAgentWork(migrationId, 'migration-prompt', payloadPath, carried);
|
|
392
|
+
}
|
|
393
|
+
else {
|
|
394
|
+
emitOrPrintPrompt(root, migration, agenticKind, {
|
|
395
|
+
persistPath: payloadPath,
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
awaitingKind = 'migration-prompt';
|
|
399
|
+
}
|
|
400
|
+
else if (generatorAlreadyCompleted &&
|
|
401
|
+
startedStep.validationOwed === true) {
|
|
402
|
+
// The persisted flag is the only record that these changes still owe a
|
|
403
|
+
// validation pass: the decision needed the generator result, which is
|
|
404
|
+
// gone with the attempt that made it, and a true waiver never writes the
|
|
405
|
+
// flag. The commit stays with the fold, as on a first attempt.
|
|
406
|
+
await reinstallFromBaseline();
|
|
407
|
+
const carried = (0, agent_work_payload_1.latestStoredAgentWorkPayload)(dir, step.id, startedStep.attempt, startedStep.generatorCompletedAtAttempt, { migrationId, kind: 'generator-validation' });
|
|
408
|
+
if (carried) {
|
|
409
|
+
reemitCarriedAgentWork(migrationId, 'generator-validation', payloadPath, carried);
|
|
410
|
+
}
|
|
411
|
+
else {
|
|
412
|
+
emitValidationBlock(root, migration, undefined, {
|
|
413
|
+
persistPath: payloadPath,
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
awaitingKind = 'generator-validation';
|
|
369
417
|
}
|
|
370
418
|
else if (generatorAlreadyCompleted) {
|
|
419
|
+
// Reached when the earlier attempt waived the AI step, or none was owed
|
|
420
|
+
// (no changes, validation off, or an older-nx state).
|
|
371
421
|
state = await finishCompletedGenerator(dir, root, state, startedStep, migration, effectiveSkipInstall, runId);
|
|
372
422
|
outcome = buildOutcome([], [], 'The generator ran in an earlier attempt; this attempt completed its install and commit.', root);
|
|
373
423
|
}
|
|
@@ -375,22 +425,57 @@ async function runRecorded(root, runId, migration, skipInstall, isVerbose) {
|
|
|
375
425
|
const installer = new execute_migration_1.ChangedDepInstaller(root, effectiveSkipInstall, `${(0, execute_migration_1.formatSingleMigrationRerunCommand)(migrationId)} --run-id=${runId}`);
|
|
376
426
|
// Read once; the run and the hybrid documentation resolution share it.
|
|
377
427
|
const resolvedCollection = (0, execute_migration_1.readMigrationCollection)(migration.package, root);
|
|
378
|
-
const { changes, nextSteps, agentContext, logs, madeChanges } = await (0, execute_migration_1.runNxOrAngularMigration)(root, migration, isVerbose,
|
|
428
|
+
const { changes, nextSteps, agentContext, skipAgentic, logs, madeChanges, } = await (0, execute_migration_1.runNxOrAngularMigration)(root, migration, isVerbose,
|
|
429
|
+
// Captured whenever an agent step may consume it: a hybrid's prompt
|
|
430
|
+
// payload, or the validation pass over the generator's changes.
|
|
431
|
+
(0, migration_shape_1.isHybridMigration)(migration) || shouldValidate, resolvedCollection);
|
|
432
|
+
// Mirrors the classic loop's waiver semantics (migrate.ts): the two must
|
|
433
|
+
// agree on when an AI step was owed for skipAgentic to waive.
|
|
434
|
+
const validationApplies = shouldValidate && !(0, migration_shape_1.isHybridMigration)(migration) && changes.length > 0;
|
|
435
|
+
const waivedAgenticStep = skipAgentic && ((0, migration_shape_1.isHybridMigration)(migration) || validationApplies);
|
|
436
|
+
const validationOwed = validationApplies && !waivedAgenticStep;
|
|
437
|
+
const promptOwed = (0, migration_shape_1.isHybridMigration)(migration) && !waivedAgenticStep;
|
|
379
438
|
// Recorded before the commit is attempted: from here on the changes are
|
|
380
439
|
// in the tree, so a failed install or commit must leave a retry with
|
|
381
|
-
// only those left to do rather than running the generator again.
|
|
440
|
+
// only those left to do rather than running the generator again. The
|
|
441
|
+
// waiver and the owed validation ride along so that retry re-emits
|
|
442
|
+
// exactly the agent work this attempt decided was owed.
|
|
382
443
|
state = transition(dir, {
|
|
383
444
|
type: 'markGeneratorCompleted',
|
|
384
445
|
stepId: step.id,
|
|
446
|
+
agenticWaived: waivedAgenticStep,
|
|
447
|
+
validationOwed,
|
|
448
|
+
madeChanges,
|
|
385
449
|
});
|
|
386
|
-
if (
|
|
450
|
+
if (waivedAgenticStep) {
|
|
451
|
+
(0, migrate_output_1.logWaivedAgenticStep)(migration, agentContext);
|
|
452
|
+
}
|
|
453
|
+
else if (!(0, migration_shape_1.isHybridMigration)(migration) && !validationApplies) {
|
|
387
454
|
forwardDroppedAgentContext(migration, agentContext, agenticKind);
|
|
388
455
|
}
|
|
456
|
+
// Stored before the install: a failed install fails the attempt, and
|
|
457
|
+
// the retry re-hands this payload instead of a context-free one.
|
|
458
|
+
const impl = { logs, changes, agentContext };
|
|
459
|
+
const owed = validationOwed
|
|
460
|
+
? validationPayload(root, migration, impl, resolvedCollection)
|
|
461
|
+
: promptOwed
|
|
462
|
+
? promptPayload(root, migration, impl, resolvedCollection)
|
|
463
|
+
: undefined;
|
|
464
|
+
if (owed) {
|
|
465
|
+
(0, agent_work_payload_1.persistAgentWorkPayload)(payloadPath, owed);
|
|
466
|
+
}
|
|
389
467
|
const install = () => recordingInstallFailure(dir, step.id, () => installer.installDepsIfChanged());
|
|
390
468
|
// Commits follow the run config, not CLI flags, and only when the
|
|
391
|
-
// generator changed something: a no-op step
|
|
392
|
-
//
|
|
393
|
-
|
|
469
|
+
// generator changed something: a no-op step's commit would absorb prior
|
|
470
|
+
// pending diffs under its name. A step handing work back defers its
|
|
471
|
+
// commit to the fold, so the migration lands as one commit and a failed
|
|
472
|
+
// hand-back leaves the changes uncommitted for review, as in the classic
|
|
473
|
+
// loop. The install still runs first: the agent may run tasks that need
|
|
474
|
+
// what the generator added.
|
|
475
|
+
if (state.createCommits &&
|
|
476
|
+
madeChanges &&
|
|
477
|
+
!validationOwed &&
|
|
478
|
+
!promptOwed) {
|
|
394
479
|
state = await commitStepChanges(dir, root, state, step, migration, install);
|
|
395
480
|
}
|
|
396
481
|
else {
|
|
@@ -403,12 +488,13 @@ async function runRecorded(root, runId, migration, skipInstall, isVerbose) {
|
|
|
403
488
|
(0, util_1.recordInstallLanded)(root, dir, step.id);
|
|
404
489
|
}
|
|
405
490
|
printNextSteps(migration, nextSteps);
|
|
406
|
-
if (
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
491
|
+
if (validationOwed) {
|
|
492
|
+
emitValidationPayload(migrationId, owed);
|
|
493
|
+
awaitingKind = 'generator-validation';
|
|
494
|
+
}
|
|
495
|
+
else if (promptOwed) {
|
|
496
|
+
emitPromptForOuterAgent(migrationId, owed, true);
|
|
497
|
+
awaitingKind = 'migration-prompt';
|
|
412
498
|
}
|
|
413
499
|
else {
|
|
414
500
|
outcome = buildOutcome(changes, nextSteps, migration.description, root);
|
|
@@ -426,14 +512,14 @@ async function runRecorded(root, runId, migration, skipInstall, isVerbose) {
|
|
|
426
512
|
});
|
|
427
513
|
throw e;
|
|
428
514
|
}
|
|
429
|
-
//
|
|
430
|
-
//
|
|
431
|
-
|
|
432
|
-
if ((0, migration_shape_1.isPromptOnlyMigration)(migration) || (0, migration_shape_1.isHybridMigration)(migration)) {
|
|
515
|
+
// Handed-back work is applied by a separate actor, so parking exits
|
|
516
|
+
// successfully rather than failing the step.
|
|
517
|
+
if (awaitingKind !== undefined) {
|
|
433
518
|
transition(dir, {
|
|
434
519
|
type: 'awaitPromptOutcome',
|
|
435
520
|
stepId: step.id,
|
|
436
521
|
finishedAt: (0, util_1.nowIso)(),
|
|
522
|
+
awaitingKind,
|
|
437
523
|
});
|
|
438
524
|
return;
|
|
439
525
|
}
|
|
@@ -478,7 +564,10 @@ async function recordingInstallFailure(dir, stepId, install) {
|
|
|
478
564
|
async function finishCompletedGenerator(dir, root, state, step, migration, skipInstall, runId) {
|
|
479
565
|
const migrationId = `${migration.package}:${migration.name}`;
|
|
480
566
|
const installDeps = () => recordingInstallFailure(dir, step.id, () => (0, util_1.installDepsChangedSinceDispense)(root, dir, step, skipInstall, `${(0, execute_migration_1.formatSingleMigrationRerunCommand)(migrationId)} --run-id=${runId}`));
|
|
481
|
-
|
|
567
|
+
// Same no-op guard as the first attempt. Only an explicit false skips the
|
|
568
|
+
// commit: absent means an older nx wrote the marker without recording the
|
|
569
|
+
// answer, and the commit is kept as that version's retries did.
|
|
570
|
+
if (!state.createCommits || step.generatorMadeChanges === false) {
|
|
482
571
|
await installDeps();
|
|
483
572
|
return state;
|
|
484
573
|
}
|
|
@@ -506,12 +595,14 @@ async function commitStepChanges(dir, root, state, step, migration, installDeps)
|
|
|
506
595
|
const entry = (0, state_machine_1.commitResultToLedgerEntry)(result, step.id, absorbedStepIds);
|
|
507
596
|
return entry ? appendCommit(dir, entry) : state;
|
|
508
597
|
}
|
|
509
|
-
//
|
|
510
|
-
//
|
|
598
|
+
// The git commit itself already ran outside the lock; only this pure append is
|
|
599
|
+
// locked. A landed entry carries the resolved issues of every step it names,
|
|
600
|
+
// same as the orchestrator's fold and adopt appends: an absorbed step's
|
|
601
|
+
// resolutions would otherwise land unattached.
|
|
511
602
|
function appendCommit(dir, entry) {
|
|
512
603
|
return (0, state_lock_1.updateRunState)(dir, (fresh) => ({
|
|
513
604
|
...fresh,
|
|
514
|
-
commits: [...fresh.commits, entry],
|
|
605
|
+
commits: [...fresh.commits, (0, issues_1.attachIssueIdsToCommitEntry)(fresh, entry)],
|
|
515
606
|
}));
|
|
516
607
|
}
|
|
517
608
|
function buildOutcome(changes, nextSteps, description, root) {
|
|
@@ -598,32 +689,60 @@ function printNextSteps(migration, nextSteps) {
|
|
|
598
689
|
bodyLines: nextSteps.map((line) => `- ${(0, text_1.singleLine)(line)}`),
|
|
599
690
|
});
|
|
600
691
|
}
|
|
601
|
-
function emitOrPrintPrompt(root, migration, agenticKind,
|
|
692
|
+
function emitOrPrintPrompt(root, migration, agenticKind, opts = {}) {
|
|
602
693
|
const migrationId = `${migration.package}:${migration.name}`;
|
|
603
|
-
const promptPath = migration.prompt;
|
|
604
|
-
const documentationPath = resolveDocumentationPath(root, migration, resolvedCollection);
|
|
605
694
|
if (agenticKind === 'inside-agent') {
|
|
606
|
-
|
|
695
|
+
const payload = promptPayload(root, migration, opts.impl, opts.resolvedCollection);
|
|
696
|
+
if (opts.persistPath) {
|
|
697
|
+
(0, agent_work_payload_1.persistAgentWorkPayload)(opts.persistPath, payload);
|
|
698
|
+
}
|
|
699
|
+
emitPromptForOuterAgent(migrationId, payload, opts.persistPath !== undefined);
|
|
607
700
|
}
|
|
608
701
|
else {
|
|
609
|
-
printPromptForUser(root, migration,
|
|
702
|
+
printPromptForUser(root, migration, migration.prompt, resolveDocumentationPath(root, migration, opts.resolvedCollection));
|
|
610
703
|
}
|
|
611
704
|
}
|
|
612
|
-
function
|
|
613
|
-
const payload = {
|
|
705
|
+
function promptPayload(root, migration, impl, resolvedCollection) {
|
|
706
|
+
const payload = {
|
|
707
|
+
migrationId: `${migration.package}:${migration.name}`,
|
|
708
|
+
prompt: migration.prompt,
|
|
709
|
+
};
|
|
710
|
+
const documentationPath = resolveDocumentationPath(root, migration, resolvedCollection);
|
|
614
711
|
if (documentationPath)
|
|
615
712
|
payload.documentationPath = documentationPath;
|
|
616
|
-
if (impl)
|
|
617
|
-
payload.impl =
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
713
|
+
if (impl)
|
|
714
|
+
payload.impl = implPayload(impl);
|
|
715
|
+
return payload;
|
|
716
|
+
}
|
|
717
|
+
const PROMPT_NOT_APPLIED = `The following prompt-based migration was not applied automatically.`;
|
|
718
|
+
// Recorded steps hand work back through the orchestrator, per the runbook's loop.
|
|
719
|
+
const RUN_NEXT_FIRST = `Run the dispensed "next" command first: its response restates this work and names the handoff file to write.`;
|
|
720
|
+
const CHANGES_AWAIT_VALIDATION = `The following migration's generator ran in an earlier attempt and its changes still await validation.`;
|
|
721
|
+
function emitPromptForOuterAgent(migrationId, payload, recorded) {
|
|
722
|
+
(0, agent_output_1.logToAgent)({
|
|
723
|
+
title: recorded
|
|
724
|
+
? `${PROMPT_NOT_APPLIED} ${RUN_NEXT_FIRST}`
|
|
725
|
+
: `${PROMPT_NOT_APPLIED} Apply it to this workspace, then continue.`,
|
|
726
|
+
});
|
|
727
|
+
(0, agent_output_1.emitPromptBlock)(migrationId, payload);
|
|
728
|
+
}
|
|
729
|
+
function implPayload(impl) {
|
|
730
|
+
return {
|
|
731
|
+
logs: impl.logs,
|
|
732
|
+
changes: impl.changes.map((c) => ({ type: c.type, path: c.path })),
|
|
733
|
+
...(impl.agentContext.length > 0
|
|
734
|
+
? { agentContext: impl.agentContext }
|
|
735
|
+
: {}),
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
// Re-hands what an earlier attempt stored. The payload is re-persisted under
|
|
739
|
+
// this attempt because the dispense reads only the current attempt's file.
|
|
740
|
+
function reemitCarriedAgentWork(migrationId, kind, payloadPath, payload) {
|
|
741
|
+
(0, agent_work_payload_1.persistAgentWorkPayload)(payloadPath, payload);
|
|
625
742
|
(0, agent_output_1.logToAgent)({
|
|
626
|
-
title:
|
|
743
|
+
title: `${kind === 'migration-prompt'
|
|
744
|
+
? PROMPT_NOT_APPLIED
|
|
745
|
+
: CHANGES_AWAIT_VALIDATION} ${RUN_NEXT_FIRST}`,
|
|
627
746
|
});
|
|
628
747
|
(0, agent_output_1.emitPromptBlock)(migrationId, payload);
|
|
629
748
|
}
|
|
@@ -660,6 +779,37 @@ function printPromptForUser(root, migration, promptPath, documentationPath) {
|
|
|
660
779
|
bodyLines,
|
|
661
780
|
});
|
|
662
781
|
}
|
|
782
|
+
// `impl` is absent on a retry whose earlier attempt left no stored payload;
|
|
783
|
+
// the emission then points at the tree.
|
|
784
|
+
function emitValidationBlock(root, migration, impl, opts = {}) {
|
|
785
|
+
const payload = validationPayload(root, migration, impl, opts.resolvedCollection);
|
|
786
|
+
if (opts.persistPath) {
|
|
787
|
+
(0, agent_work_payload_1.persistAgentWorkPayload)(opts.persistPath, payload);
|
|
788
|
+
}
|
|
789
|
+
emitValidationPayload(`${migration.package}:${migration.name}`, payload);
|
|
790
|
+
}
|
|
791
|
+
function emitValidationPayload(migrationId, payload) {
|
|
792
|
+
(0, agent_output_1.logToAgent)({
|
|
793
|
+
title: payload.impl !== undefined
|
|
794
|
+
? `The following migration's generator ran without an AI-driven part. ${RUN_NEXT_FIRST}`
|
|
795
|
+
: `${CHANGES_AWAIT_VALIDATION} ${RUN_NEXT_FIRST} Inspect the changes with git.`,
|
|
796
|
+
});
|
|
797
|
+
(0, agent_output_1.emitPromptBlock)(migrationId, payload);
|
|
798
|
+
}
|
|
799
|
+
function validationPayload(root, migration, impl, resolvedCollection) {
|
|
800
|
+
// `kind` is what tells the block apart from an applied-prompt payload,
|
|
801
|
+
// which carries `prompt` instead.
|
|
802
|
+
const payload = {
|
|
803
|
+
migrationId: `${migration.package}:${migration.name}`,
|
|
804
|
+
kind: 'generator-validation',
|
|
805
|
+
};
|
|
806
|
+
const documentationPath = resolveDocumentationPath(root, migration, resolvedCollection);
|
|
807
|
+
if (documentationPath)
|
|
808
|
+
payload.documentationPath = documentationPath;
|
|
809
|
+
if (impl)
|
|
810
|
+
payload.impl = implPayload(impl);
|
|
811
|
+
return payload;
|
|
812
|
+
}
|
|
663
813
|
// Non-fatal: documentation is supplementary, so a failure warns and the
|
|
664
814
|
// prompt still runs without it.
|
|
665
815
|
function resolveDocumentationPath(root, migration, resolvedCollection) {
|
|
@@ -249,7 +249,11 @@ function utmContentForCommand(command) {
|
|
|
249
249
|
async function nxCloudPrompt(key, utmContent) {
|
|
250
250
|
const { message, choices, initial, footer, hint } = ab_testing_1.messages.getPrompt(key);
|
|
251
251
|
// No separate footer/hint slot, so both are folded into the message.
|
|
252
|
-
const suffix = [
|
|
252
|
+
const suffix = [
|
|
253
|
+
hint,
|
|
254
|
+
footer && `${footer} ${(0, ab_testing_1.nxCloudHyperlink)(utmContent)}`,
|
|
255
|
+
`See it in action: ${(0, ab_testing_1.nxCloudDemoHyperlink)(utmContent)}`,
|
|
256
|
+
]
|
|
253
257
|
.filter(Boolean)
|
|
254
258
|
.map((t) => pc.dim(t));
|
|
255
259
|
return (await (0, prompt_helpers_1.selectPrompt)({
|
|
@@ -207,15 +207,22 @@ async function runPublishOnProjects(args, projectGraph, nxJson, projectNames, ex
|
|
|
207
207
|
* NOTE: Force TUI to be disabled for now.
|
|
208
208
|
*/
|
|
209
209
|
process.env.NX_TUI = 'false';
|
|
210
|
+
const publishOutputStyle = args.specifiedOutputStyle ?? 'static';
|
|
210
211
|
const { taskResults } = await (0, run_command_1.runCommandForTasks)(projectsWithTarget, projectGraph, { nxJson }, {
|
|
211
212
|
targets: [requiredTargetName],
|
|
213
|
+
...args,
|
|
212
214
|
// Everything this command reports — the registry, the tag, the
|
|
213
215
|
// package.json diff, the dry-run summary — is printed from inside the
|
|
214
216
|
// task, so the failures-only default would swallow all of it (under
|
|
215
|
-
// --dry-run every task succeeds by definition).
|
|
216
|
-
//
|
|
217
|
-
|
|
218
|
-
|
|
217
|
+
// --dry-run every task succeeds by definition). Set after the spread and
|
|
218
|
+
// on all three fields: the middleware always writes `resolvedOutputStyle`,
|
|
219
|
+
// so a value placed before it would be overwritten on every run, and the
|
|
220
|
+
// renderer reads the new fields rather than the deprecated one. A style
|
|
221
|
+
// the user actually named still wins, which is what `specifiedOutputStyle`
|
|
222
|
+
// being set means.
|
|
223
|
+
outputStyle: publishOutputStyle,
|
|
224
|
+
specifiedOutputStyle: publishOutputStyle,
|
|
225
|
+
resolvedOutputStyle: publishOutputStyle,
|
|
219
226
|
// It is possible for workspaces to have circular dependencies between packages and still release them to a registry
|
|
220
227
|
nxIgnoreCycles: true,
|
|
221
228
|
}, overrides, null, {}, extraOptions);
|
|
@@ -227,14 +227,14 @@ class GithubRemoteReleaseClient extends remote_release_client_1.RemoteReleaseCli
|
|
|
227
227
|
`---`,
|
|
228
228
|
`Request Data:`,
|
|
229
229
|
`Repo: ${this.getRemoteRepoData()?.slug}`,
|
|
230
|
-
`Token Header
|
|
230
|
+
`Token Header: ${this.getRedactedTokenHeader()}`,
|
|
231
231
|
`Body: ${JSON.stringify(result.requestData)}`,
|
|
232
232
|
],
|
|
233
233
|
});
|
|
234
234
|
}
|
|
235
235
|
else {
|
|
236
|
-
console.log(error);
|
|
237
|
-
console.error(`An unknown error occurred while trying to create a release on GitHub, please report this on https://github.com/nrwl/nx (NOTE:
|
|
236
|
+
console.log(this.inspectWithRedactedToken(error));
|
|
237
|
+
console.error(`An unknown error occurred while trying to create a release on GitHub, please report this on https://github.com/nrwl/nx (NOTE: your GitHub token is redacted above, but please double-check before sharing)`);
|
|
238
238
|
}
|
|
239
239
|
}
|
|
240
240
|
const shouldContinueInGitHub = await this.promptForContinueInGitHub();
|
|
@@ -143,14 +143,14 @@ class GitLabRemoteReleaseClient extends remote_release_client_1.RemoteReleaseCli
|
|
|
143
143
|
`---`,
|
|
144
144
|
`Request Data:`,
|
|
145
145
|
`Repo: ${this.getRemoteRepoData()?.slug}`,
|
|
146
|
-
`Token Header
|
|
146
|
+
`Token Header: ${this.getRedactedTokenHeader()}`,
|
|
147
147
|
`Body: ${JSON.stringify(result.requestData)}`,
|
|
148
148
|
],
|
|
149
149
|
});
|
|
150
150
|
}
|
|
151
151
|
else {
|
|
152
|
-
console.log(error);
|
|
153
|
-
console.error(`An unknown error occurred while trying to create a release on GitLab, please report this on https://github.com/nrwl/nx (NOTE:
|
|
152
|
+
console.log(this.inspectWithRedactedToken(error));
|
|
153
|
+
console.error(`An unknown error occurred while trying to create a release on GitLab, please report this on https://github.com/nrwl/nx (NOTE: your GitLab token is redacted above, but please double-check before sharing)`);
|
|
154
154
|
}
|
|
155
155
|
}
|
|
156
156
|
const shouldContinueInGitLab = await this.promptForContinueInGitLab();
|
package/dist/src/command-line/release/utils/remote-release-clients/remote-release-client.d.ts
CHANGED
|
@@ -44,6 +44,8 @@ export declare abstract class RemoteReleaseClient<RemoteRelease extends Record<s
|
|
|
44
44
|
headerName: string;
|
|
45
45
|
} | null);
|
|
46
46
|
getRemoteRepoData<T extends RemoteRepoData>(): T | null;
|
|
47
|
+
protected inspectWithRedactedToken(error: unknown): string;
|
|
48
|
+
protected getRedactedTokenHeader(): string;
|
|
47
49
|
/**
|
|
48
50
|
* Create a post git task that will be executed by nx release changelog after performing any relevant
|
|
49
51
|
* git operations, if the user has opted into remote release creation.
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.RemoteReleaseClient = void 0;
|
|
4
4
|
exports.createRemoteReleaseClient = createRemoteReleaseClient;
|
|
5
5
|
const tslib_1 = require("tslib");
|
|
6
|
+
const node_util_1 = require("node:util");
|
|
6
7
|
const axios_1 = tslib_1.__importDefault(require("axios"));
|
|
7
8
|
const handle_import_1 = require("../../../../utils/handle-import");
|
|
8
9
|
const print_changes_1 = require("../print-changes");
|
|
@@ -30,6 +31,40 @@ class RemoteReleaseClient {
|
|
|
30
31
|
getRemoteRepoData() {
|
|
31
32
|
return this.remoteRepoData;
|
|
32
33
|
}
|
|
34
|
+
inspectWithRedactedToken(error) {
|
|
35
|
+
const inspected = (0, node_util_1.inspect)(error);
|
|
36
|
+
// The dump can hold a different string than the raw token: axios trims
|
|
37
|
+
// header values and inspect() escapes the result, while message/stack keep
|
|
38
|
+
// it bare. Redact every rendering; the Set collapses them for an ordinary
|
|
39
|
+
// token. The CR/LF needle is belt-and-braces — node rejects such headers.
|
|
40
|
+
const token = this.tokenData?.token?.trim();
|
|
41
|
+
if (!token) {
|
|
42
|
+
return inspected;
|
|
43
|
+
}
|
|
44
|
+
const needles = new Set([
|
|
45
|
+
token,
|
|
46
|
+
token.replace(/[\r\n]/g, ''),
|
|
47
|
+
(0, node_util_1.inspect)(token).slice(1, -1),
|
|
48
|
+
]);
|
|
49
|
+
// inspect() renders a long string carrying a line break as concatenated
|
|
50
|
+
// per-line chunks, which no whole-token needle spans. Redact the lines too,
|
|
51
|
+
// skipping fragments short enough to collide with ordinary dump text.
|
|
52
|
+
for (const line of token.split(/[\r\n]+/)) {
|
|
53
|
+
if (line.length >= 8) {
|
|
54
|
+
needles.add(line);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return [...needles].reduce((text, needle) => text.split(needle).join('<redacted>'), inspected);
|
|
58
|
+
}
|
|
59
|
+
getRedactedTokenHeader() {
|
|
60
|
+
if (!this.tokenData) {
|
|
61
|
+
return 'none';
|
|
62
|
+
}
|
|
63
|
+
const { headerName } = this.tokenData;
|
|
64
|
+
return headerName === 'Authorization'
|
|
65
|
+
? `${headerName}: Bearer <redacted>`
|
|
66
|
+
: `${headerName}: <redacted>`;
|
|
67
|
+
}
|
|
33
68
|
/**
|
|
34
69
|
* Make an (optionally authenticated) API request to the remote release provider
|
|
35
70
|
*/
|
|
@@ -67,7 +67,7 @@ export declare function withOverrides<T extends {
|
|
|
67
67
|
}>(args: T, commandLevel?: number): T & {
|
|
68
68
|
__overrides_unparsed__: string[];
|
|
69
69
|
};
|
|
70
|
-
declare const allOutputStyles: readonly ['tui', 'dynamic', 'dynamic-legacy', 'static', 'static-failures-only', 'stream', 'stream-without-prefixes'];
|
|
70
|
+
declare const allOutputStyles: readonly ['tui', 'dynamic', 'dynamic-legacy', 'static', 'static-failures-only', 'summary', 'stream', 'stream-without-prefixes'];
|
|
71
71
|
export type OutputStyle = (typeof allOutputStyles)[number];
|
|
72
72
|
export declare function withOutputStyleOption<T>(yargs: Argv<T>, choices?: ReadonlyArray<OutputStyle>): Argv<T & {
|
|
73
73
|
outputStyle: OutputStyle;
|
|
@@ -17,6 +17,7 @@ exports.parseNewlines = parseNewlines;
|
|
|
17
17
|
exports.parseCSV = parseCSV;
|
|
18
18
|
exports.readParallelFromArgsAndEnv = readParallelFromArgsAndEnv;
|
|
19
19
|
const nx_json_1 = require("../../config/nx-json");
|
|
20
|
+
const native_1 = require("../../native");
|
|
20
21
|
const is_tui_enabled_1 = require("../../tasks-runner/is-tui-enabled");
|
|
21
22
|
const node_os_1 = require("node:os");
|
|
22
23
|
exports.defaultYargsParserConfiguration = {
|
|
@@ -273,6 +274,7 @@ const allOutputStyles = [
|
|
|
273
274
|
'dynamic-legacy',
|
|
274
275
|
'static',
|
|
275
276
|
'static-failures-only',
|
|
277
|
+
'summary',
|
|
276
278
|
'stream',
|
|
277
279
|
'stream-without-prefixes',
|
|
278
280
|
];
|
|
@@ -282,12 +284,13 @@ function withOutputStyleOption(yargs, choices = [
|
|
|
282
284
|
'tui',
|
|
283
285
|
'static',
|
|
284
286
|
'static-failures-only',
|
|
287
|
+
'summary',
|
|
285
288
|
'stream',
|
|
286
289
|
'stream-without-prefixes',
|
|
287
290
|
]) {
|
|
288
291
|
return yargs
|
|
289
292
|
.option('outputStyle', {
|
|
290
|
-
describe: `Defines how Nx emits outputs tasks logs. **tui**: enables the Nx Terminal UI, recommended for local development environments. **dynamic-legacy**: use dynamic-legacy output life cycle, previous content is overwritten or modified as new outputs are added, display minimal logs by default, always show errors. This output format is recommended for local development environments where tui is not supported. **static**: uses static output life cycle, no previous content is rewritten or modified as new outputs are added, and every task prints its full output regardless of status. **static-failures-only**: same as **static**, but successful and cached tasks collapse to a single line, so only failing tasks (and, for a single-project run, the project you asked for) print their full output. This is what Nx uses by default in CI and other non-interactive environments. **stream**: nx by default logs output to an internal output stream, enable this option to stream logs to stdout / stderr. **stream-without-prefixes**: nx prefixes the project name the target is running on, use this option remove the project name prefix from output.`,
|
|
293
|
+
describe: `Defines how Nx emits outputs tasks logs. **tui**: enables the Nx Terminal UI, recommended for local development environments. **dynamic-legacy**: use dynamic-legacy output life cycle, previous content is overwritten or modified as new outputs are added, display minimal logs by default, always show errors. This output format is recommended for local development environments where tui is not supported. **static**: uses static output life cycle, no previous content is rewritten or modified as new outputs are added, and every task prints its full output regardless of status. **static-failures-only**: same as **static**, but successful and cached tasks collapse to a single line, so only failing tasks (and, for a single-project run, the project you asked for) print their full output. This is what Nx uses by default in CI and other non-interactive environments. **summary**: prints only run counts and one line per failing task, naming the file on disk that holds its full output. No task output is printed inline, so the size of a run's output does not depend on how much its tasks logged. This is the default when Nx detects it is being driven by an AI agent. **stream**: nx by default logs output to an internal output stream, enable this option to stream logs to stdout / stderr. **stream-without-prefixes**: nx prefixes the project name the target is running on, use this option remove the project name prefix from output.`,
|
|
291
294
|
type: 'string',
|
|
292
295
|
choices,
|
|
293
296
|
})
|
|
@@ -298,16 +301,31 @@ function withOutputStyleOption(yargs, choices = [
|
|
|
298
301
|
choices.includes(process.env.NX_DEFAULT_OUTPUT_STYLE)) {
|
|
299
302
|
args.outputStyle = process.env.NX_DEFAULT_OUTPUT_STYLE;
|
|
300
303
|
}
|
|
304
|
+
// Everything above this line is the user naming a style; everything
|
|
305
|
+
// below infers one. Splitting them here is what lets the rest of the
|
|
306
|
+
// codebase ask either question without re-deriving it from a single
|
|
307
|
+
// overloaded field.
|
|
308
|
+
args.specifiedOutputStyle = args.outputStyle;
|
|
301
309
|
},
|
|
302
310
|
(args) => {
|
|
311
|
+
// Resolution, in precedence order. `shouldUseTui` reads
|
|
312
|
+
// `specifiedOutputStyle`, so it sees only what the user named and is
|
|
313
|
+
// not fed a value inferred here - which is what used to make this
|
|
314
|
+
// ordering circular. It also reads `NX_TUI` before its own agent check,
|
|
315
|
+
// so an explicit opt-in beats the agent default without needing a
|
|
316
|
+
// special case: agent detection is ambient (`REPL_ID` is exported into
|
|
317
|
+
// a human's shell), and a human asking for a renderer outranks it.
|
|
303
318
|
const useTui = (0, is_tui_enabled_1.shouldUseTui)((0, nx_json_1.readNxJson)(), args);
|
|
319
|
+
process.env.NX_TUI = useTui.toString();
|
|
320
|
+
const resolved = args.outputStyle ??
|
|
321
|
+
(useTui ? 'tui' : (0, native_1.isAiAgent)() ? 'summary' : 'static-failures-only');
|
|
322
|
+
args.resolvedOutputStyle = resolved;
|
|
323
|
+
// `outputStyle` stays what yargs parsed, except for the TUI, whose
|
|
324
|
+
// `check` runs after kebab-case normalization and needs both spellings.
|
|
304
325
|
if (useTui) {
|
|
305
|
-
// We have to set both of these because `check` runs after the normalization that
|
|
306
|
-
// handles the kebab-case'd args -> camelCase'd args translation.
|
|
307
326
|
args['output-style'] = 'tui';
|
|
308
327
|
args.outputStyle = 'tui';
|
|
309
328
|
}
|
|
310
|
-
process.env.NX_TUI = useTui.toString();
|
|
311
329
|
},
|
|
312
330
|
]);
|
|
313
331
|
}
|