nx 23.2.0-beta.4 → 23.2.0-beta.6
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/nx.js +1 -0
- package/dist/src/command-line/affected/affected.js +1 -0
- package/dist/src/command-line/configure-ai-agents/configure-ai-agents.js +2 -2
- package/dist/src/command-line/init/init-v2.js +2 -2
- package/dist/src/command-line/migrate/agentic/definitions.js +2 -2
- package/dist/src/command-line/migrate/agentic/handoff-gitignore.d.ts +15 -19
- package/dist/src/command-line/migrate/agentic/handoff-gitignore.js +19 -31
- package/dist/src/command-line/migrate/agentic/handoff.d.ts +4 -6
- package/dist/src/command-line/migrate/agentic/handoff.js +9 -8
- package/dist/src/command-line/migrate/agentic/print-dropped-agent-context.d.ts +7 -12
- package/dist/src/command-line/migrate/agentic/print-dropped-agent-context.js +7 -0
- package/dist/src/command-line/migrate/agentic/run-step.d.ts +10 -0
- package/dist/src/command-line/migrate/agentic/select.d.ts +10 -2
- package/dist/src/command-line/migrate/agentic/select.js +10 -2
- package/dist/src/command-line/migrate/agentic/types.d.ts +17 -3
- package/dist/src/command-line/migrate/agentic/types.js +26 -0
- package/dist/src/command-line/migrate/command-object.d.ts +3 -0
- package/dist/src/command-line/migrate/command-object.js +6 -1
- package/dist/src/command-line/migrate/execute-migration.d.ts +14 -9
- package/dist/src/command-line/migrate/execute-migration.js +82 -18
- package/dist/src/command-line/migrate/migrate-analytics.d.ts +7 -0
- package/dist/src/command-line/migrate/migrate-analytics.js +14 -0
- package/dist/src/command-line/migrate/migrate-commits.d.ts +32 -8
- package/dist/src/command-line/migrate/migrate-commits.js +77 -9
- package/dist/src/command-line/migrate/migrate-config.d.ts +11 -12
- package/dist/src/command-line/migrate/migrate-config.js +14 -14
- package/dist/src/command-line/migrate/migrate-output.d.ts +45 -7
- package/dist/src/command-line/migrate/migrate-output.js +52 -8
- package/dist/src/command-line/migrate/migrate-ui-api.d.ts +2 -1
- package/dist/src/command-line/migrate/migrate-ui-api.js +16 -7
- package/dist/src/command-line/migrate/migrate.d.ts +17 -68
- package/dist/src/command-line/migrate/migrate.js +411 -229
- package/dist/src/command-line/migrate/migration-shape.d.ts +8 -0
- package/dist/src/command-line/migrate/run/index.d.ts +2 -0
- package/dist/src/command-line/migrate/run/index.js +5 -0
- package/dist/src/command-line/migrate/run/util.d.ts +1 -0
- package/dist/src/command-line/migrate/run/util.js +16 -0
- package/dist/src/command-line/migrate/run/worker.d.ts +15 -0
- package/dist/src/command-line/migrate/run/worker.js +419 -0
- package/dist/src/command-line/migrate/run-migration-process.js +6 -5
- package/dist/src/command-line/migrate/sort-migrations.d.ts +16 -0
- package/dist/src/command-line/migrate/sort-migrations.js +32 -0
- package/dist/src/command-line/migrate/version-skew-guard.d.ts +58 -0
- package/dist/src/command-line/migrate/version-skew-guard.js +155 -0
- package/dist/src/command-line/nx-cloud/start-nx-agents/command-object.d.ts +2 -0
- package/dist/src/command-line/nx-cloud/start-nx-agents/command-object.js +16 -0
- package/dist/src/command-line/nx-cloud/start-nx-agents/start-nx-agents.d.ts +4 -0
- package/dist/src/command-line/nx-cloud/start-nx-agents/start-nx-agents.js +13 -0
- package/dist/src/command-line/nx-commands.js +26 -24
- package/dist/src/command-line/release/changelog.js +5 -0
- package/dist/src/command-line/release/utils/git.js +1 -1
- package/dist/src/command-line/release/utils/release-graph.d.ts +1 -0
- package/dist/src/command-line/release/utils/release-graph.js +15 -3
- package/dist/src/command-line/release/version/version-actions.d.ts +8 -0
- package/dist/src/command-line/release/version/version-actions.js +8 -0
- package/dist/src/command-line/release/version.js +9 -5
- package/dist/src/command-line/reset/reset.js +6 -1
- package/dist/src/command-line/run/run-one.js +1 -0
- package/dist/src/command-line/run-many/run-many.js +1 -0
- package/dist/src/config/misc-interfaces.d.ts +19 -7
- package/dist/src/core/graph/main.js +1 -1
- package/dist/src/core/graph/styles.js +1 -1
- package/dist/src/daemon/client/client.d.ts +45 -1
- package/dist/src/daemon/client/client.js +128 -32
- package/dist/src/daemon/client/daemon-socket-messenger.js +4 -0
- package/dist/src/daemon/message-types/daemon-message.d.ts +32 -0
- package/dist/src/daemon/message-types/daemon-message.js +25 -0
- package/dist/src/daemon/server/latest-nx.js +5 -2
- package/dist/src/daemon/server/server.js +22 -0
- package/dist/src/daemon/server/shutdown-utils.d.ts +5 -0
- package/dist/src/daemon/server/shutdown-utils.js +11 -4
- package/dist/src/daemon/socket-utils.d.ts +1 -0
- package/dist/src/daemon/socket-utils.js +24 -3
- package/dist/src/daemon/tmp-dir.d.ts +43 -3
- package/dist/src/daemon/tmp-dir.js +437 -15
- package/dist/src/devkit-internals.d.ts +92 -3
- package/dist/src/devkit-internals.js +189 -5
- package/dist/src/generators/internal-utils/format-changed-files-with-prettier-if-available.d.ts +5 -0
- package/dist/src/generators/internal-utils/format-changed-files-with-prettier-if-available.js +9 -2
- package/dist/src/generators/tree.d.ts +4 -2
- package/dist/src/native/index.d.ts +10 -0
- package/dist/src/native/index.js +71 -28
- package/dist/src/native/native-bindings.js +1 -0
- package/dist/src/native/native-file-cache-location.d.ts +17 -1
- package/dist/src/native/native-file-cache-location.js +86 -12
- package/dist/src/native/nx.wasm32-wasi.debug.wasm +0 -0
- package/dist/src/native/nx.wasm32-wasi.wasm +0 -0
- package/dist/src/nx-cloud/utilities/get-cloud-options.js +5 -2
- package/dist/src/nx-cloud/utilities/onboarding.js +5 -2
- package/dist/src/nx-cloud/utilities/url-shorten.d.ts +1 -4
- package/dist/src/nx-cloud/utilities/url-shorten.js +9 -8
- package/dist/src/plugins/js/lock-file/project-graph-pruning.js +14 -8
- package/dist/src/plugins/js/project-graph/build-dependencies/target-project-locator.js +2 -0
- package/dist/src/project-graph/affected/affected-project-graph-models.d.ts +1 -1
- package/dist/src/project-graph/affected/affected-project-graph.d.ts +1 -1
- package/dist/src/project-graph/affected/affected-project-graph.js +2 -2
- package/dist/src/project-graph/affected/locators/project-glob-changes.js +5 -2
- package/dist/src/project-graph/plugins/isolation/isolated-plugin.d.ts +1 -0
- package/dist/src/project-graph/plugins/isolation/isolated-plugin.js +18 -1
- package/dist/src/project-graph/plugins/isolation/messaging.d.ts +16 -3
- package/dist/src/project-graph/plugins/isolation/messaging.js +3 -0
- package/dist/src/project-graph/plugins/isolation/plugin-worker.js +58 -8
- package/dist/src/project-graph/project-graph.js +20 -0
- package/dist/src/tasks-runner/life-cycles/performance-report.d.ts +12 -0
- package/dist/src/tasks-runner/life-cycles/performance-report.js +62 -3
- package/dist/src/tasks-runner/run-command.js +4 -0
- package/dist/src/utils/assert-workspace-validity.js +7 -2
- package/dist/src/utils/child-process.d.ts +31 -1
- package/dist/src/utils/child-process.js +103 -0
- package/dist/src/utils/ignore.d.ts +128 -1
- package/dist/src/utils/ignore.js +182 -12
- package/dist/src/utils/nx-tmp-dir.d.ts +18 -0
- package/dist/src/utils/nx-tmp-dir.js +40 -0
- package/dist/src/utils/owned-private-dir.d.ts +132 -0
- package/dist/src/utils/owned-private-dir.js +323 -0
- package/dist/src/utils/shell-quoting.d.ts +15 -0
- package/dist/src/utils/shell-quoting.js +35 -0
- package/dist/src/utils/wait-for-socket-connection.d.ts +11 -0
- package/dist/src/utils/wait-for-socket-connection.js +14 -8
- package/package.json +12 -12
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";(self.webpackChunk_nx_graph_client=self.webpackChunk_nx_graph_client||[]).push([[869],{
|
|
1
|
+
"use strict";(self.webpackChunk_nx_graph_client=self.webpackChunk_nx_graph_client||[]).push([[869],{946(){}},e=>{var n;n=946,e(e.s=n)}]);
|
|
@@ -8,6 +8,11 @@ import { ConfigurationSourceMaps } from '../../project-graph/utils/project-confi
|
|
|
8
8
|
import type { FlushSyncGeneratorChangesResult, SyncGeneratorRunResult } from '../../utils/sync-generators';
|
|
9
9
|
import { type ConfigureAiAgentsStatusResponse } from '../message-types/configure-ai-agents';
|
|
10
10
|
import { type NxConsoleStatusResponse, type SetNxConsolePreferenceAndInstallResponse } from '../message-types/nx-console';
|
|
11
|
+
/** A refused connect: the errno, and the path it was made against. */
|
|
12
|
+
type ConnectRefusal = {
|
|
13
|
+
error: NodeJS.ErrnoException;
|
|
14
|
+
socketPath: string;
|
|
15
|
+
};
|
|
11
16
|
export type UnregisterCallback = () => void;
|
|
12
17
|
export type ChangedFile = {
|
|
13
18
|
path: string;
|
|
@@ -93,6 +98,12 @@ export declare class DaemonClient {
|
|
|
93
98
|
resetConfigureAiAgentsStatus(): Promise<{
|
|
94
99
|
success: boolean;
|
|
95
100
|
}>;
|
|
101
|
+
/**
|
|
102
|
+
* The pre-start probe, returning why it failed rather than leaving it on the
|
|
103
|
+
* instance: `_daemonStatus` does not serialize `isServerAvailable`'s five
|
|
104
|
+
* public callers against `startDaemonIfNecessary`.
|
|
105
|
+
*/
|
|
106
|
+
private probeServer;
|
|
96
107
|
isServerAvailable(): Promise<boolean>;
|
|
97
108
|
private startDaemonIfNecessary;
|
|
98
109
|
private sendToDaemonViaQueue;
|
|
@@ -108,8 +119,41 @@ export declare class DaemonClient {
|
|
|
108
119
|
private sendMessageToDaemon;
|
|
109
120
|
private registerDaemonProcessWithMetricsService;
|
|
110
121
|
private handleMessage;
|
|
111
|
-
|
|
122
|
+
/**
|
|
123
|
+
* @param probeRefusal what the caller's pre-start probe saw. The only evidence
|
|
124
|
+
* when a daemon refuses us and then exits — the poll cannot reproduce it
|
|
125
|
+
* once the process json is gone. `nx daemon --start` passes nothing.
|
|
126
|
+
*/
|
|
127
|
+
startInBackground(probeRefusal?: ConnectRefusal): Promise<ChildProcess['pid']>;
|
|
112
128
|
stop(): Promise<void>;
|
|
113
129
|
}
|
|
114
130
|
export declare const daemonClient: DaemonClient;
|
|
115
131
|
export declare function isDaemonEnabled(): boolean;
|
|
132
|
+
/**
|
|
133
|
+
* EACCES and EPERM are the two errnos that mean the OS refused us rather than
|
|
134
|
+
* that nothing was listening. They need opposite remedies — a socket owned by
|
|
135
|
+
* someone else versus a sandbox refusing the connect syscall — but they share
|
|
136
|
+
* the property that retrying cannot change the answer.
|
|
137
|
+
*/
|
|
138
|
+
export declare function isPermissionErrno(error: NodeJS.ErrnoException): boolean;
|
|
139
|
+
/**
|
|
140
|
+
* The operating system refused the connection. Most often the socket belongs to
|
|
141
|
+
* another user, which is the guarantee the owner-only socket directory buys —
|
|
142
|
+
* but a sandbox that denies unix-socket connects produces the same errno, so the
|
|
143
|
+
* message does not assert which. Either way it is an environment condition
|
|
144
|
+
* rather than a defect in Nx, and it deliberately does not
|
|
145
|
+
* carry `internalDaemonError`: that tag tells the user to file an issue and
|
|
146
|
+
* disables the daemon until `nx reset`, which would outlast the stale socket
|
|
147
|
+
* that caused it.
|
|
148
|
+
*
|
|
149
|
+
* It also skips the daemon log that `daemonProcessException` appends. The log
|
|
150
|
+
* belongs to *our* daemon; the process holding this socket is someone else's, so
|
|
151
|
+
* quoting it would describe an unrelated run.
|
|
152
|
+
*/
|
|
153
|
+
export declare function daemonPermissionException(socketPath: string, cause: string): Error;
|
|
154
|
+
/**
|
|
155
|
+
* Exported for testing: the `internalDaemonError` tag decides whether a daemon
|
|
156
|
+
* failure degrades to a daemonless graph build or aborts the command.
|
|
157
|
+
*/
|
|
158
|
+
export declare function daemonProcessException(message: string): Error;
|
|
159
|
+
export {};
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.daemonClient = exports.DaemonClient = void 0;
|
|
4
4
|
exports.isDaemonEnabled = isDaemonEnabled;
|
|
5
|
+
exports.isPermissionErrno = isPermissionErrno;
|
|
6
|
+
exports.daemonPermissionException = daemonPermissionException;
|
|
7
|
+
exports.daemonProcessException = daemonProcessException;
|
|
5
8
|
const child_process_1 = require("child_process");
|
|
6
9
|
const net_1 = require("net");
|
|
7
10
|
const node_fs_1 = require("node:fs");
|
|
@@ -276,9 +279,9 @@ class DaemonClient {
|
|
|
276
279
|
// Wait for daemon server to be available before trying to reconnect
|
|
277
280
|
let serverAvailable;
|
|
278
281
|
try {
|
|
279
|
-
serverAvailable = await this.waitForServerToBeAvailable({
|
|
282
|
+
({ available: serverAvailable } = await this.waitForServerToBeAvailable({
|
|
280
283
|
ignoreVersionMismatch: false,
|
|
281
|
-
});
|
|
284
|
+
}));
|
|
282
285
|
}
|
|
283
286
|
catch (err) {
|
|
284
287
|
// Version mismatch - pass error to callbacks so they can handle it
|
|
@@ -428,9 +431,9 @@ class DaemonClient {
|
|
|
428
431
|
// Wait for daemon server to be available before trying to reconnect
|
|
429
432
|
let serverAvailable;
|
|
430
433
|
try {
|
|
431
|
-
serverAvailable = await this.waitForServerToBeAvailable({
|
|
434
|
+
({ available: serverAvailable } = await this.waitForServerToBeAvailable({
|
|
432
435
|
ignoreVersionMismatch: false,
|
|
433
|
-
});
|
|
436
|
+
}));
|
|
434
437
|
}
|
|
435
438
|
catch (err) {
|
|
436
439
|
// Version mismatch - pass error to callbacks so they can handle it
|
|
@@ -665,30 +668,40 @@ class DaemonClient {
|
|
|
665
668
|
};
|
|
666
669
|
return this.sendToDaemonViaQueue(message);
|
|
667
670
|
}
|
|
668
|
-
|
|
671
|
+
/**
|
|
672
|
+
* The pre-start probe, returning why it failed rather than leaving it on the
|
|
673
|
+
* instance: `_daemonStatus` does not serialize `isServerAvailable`'s five
|
|
674
|
+
* public callers against `startDaemonIfNecessary`.
|
|
675
|
+
*/
|
|
676
|
+
async probeServer() {
|
|
669
677
|
return new Promise((resolve, reject) => {
|
|
670
678
|
try {
|
|
671
679
|
const socketPath = this.getSocketPath();
|
|
672
680
|
if (!socketPath) {
|
|
673
|
-
resolve(false);
|
|
681
|
+
resolve({ available: false });
|
|
674
682
|
return;
|
|
675
683
|
}
|
|
676
684
|
const socket = (0, net_1.connect)(socketPath, () => {
|
|
677
685
|
socket.destroy();
|
|
678
|
-
resolve(true);
|
|
686
|
+
resolve({ available: true });
|
|
679
687
|
});
|
|
680
|
-
socket.once('error', () => {
|
|
681
|
-
|
|
688
|
+
socket.once('error', (err) => {
|
|
689
|
+
// The only place the errno for "the socket is there but refuses us"
|
|
690
|
+
// is produced, and the only thing separating it from "no daemon yet".
|
|
691
|
+
resolve({ available: false, refusal: { error: err, socketPath } });
|
|
682
692
|
});
|
|
683
693
|
}
|
|
684
694
|
catch (err) {
|
|
685
695
|
if (err instanceof daemon_socket_messenger_1.VersionMismatchError) {
|
|
686
696
|
reject(err); // Let version mismatch bubble up
|
|
687
697
|
}
|
|
688
|
-
resolve(false);
|
|
698
|
+
resolve({ available: false });
|
|
689
699
|
}
|
|
690
700
|
});
|
|
691
701
|
}
|
|
702
|
+
async isServerAvailable() {
|
|
703
|
+
return (await this.probeServer()).available;
|
|
704
|
+
}
|
|
692
705
|
async startDaemonIfNecessary() {
|
|
693
706
|
if (this._daemonStatus == DaemonStatus.CONNECTED) {
|
|
694
707
|
return;
|
|
@@ -697,21 +710,22 @@ class DaemonClient {
|
|
|
697
710
|
if (this._daemonStatus == DaemonStatus.DISCONNECTED) {
|
|
698
711
|
this._daemonStatus = DaemonStatus.CONNECTING;
|
|
699
712
|
let daemonPid = null;
|
|
700
|
-
let
|
|
713
|
+
let probe;
|
|
701
714
|
try {
|
|
702
|
-
|
|
715
|
+
probe = await this.probeServer();
|
|
703
716
|
}
|
|
704
717
|
catch (err) {
|
|
705
718
|
// Version mismatch - treat as server not available, start new one
|
|
706
719
|
if (err instanceof daemon_socket_messenger_1.VersionMismatchError) {
|
|
707
|
-
|
|
720
|
+
probe = { available: false };
|
|
708
721
|
}
|
|
709
722
|
else {
|
|
710
723
|
throw err;
|
|
711
724
|
}
|
|
712
725
|
}
|
|
713
|
-
if (!
|
|
714
|
-
|
|
726
|
+
if (!probe.available) {
|
|
727
|
+
// Carried as a value so no other caller's probe can substitute for it.
|
|
728
|
+
daemonPid = await this.startInBackground(probe.refusal);
|
|
715
729
|
}
|
|
716
730
|
this.setUpConnection();
|
|
717
731
|
this._daemonStatus = DaemonStatus.CONNECTED;
|
|
@@ -762,6 +776,11 @@ class DaemonClient {
|
|
|
762
776
|
if (err.message.startsWith('connect ENOENT')) {
|
|
763
777
|
error = daemonProcessException('The Daemon Server is not running');
|
|
764
778
|
}
|
|
779
|
+
else if (isPermissionErrno(err)) {
|
|
780
|
+
// The 0700 dir and 0600 socket mean the OS refuses this rather than the
|
|
781
|
+
// connect silently succeeding.
|
|
782
|
+
error = daemonPermissionException(socketPath, err.message);
|
|
783
|
+
}
|
|
765
784
|
else if (err.message.startsWith('connect ECONNREFUSED')) {
|
|
766
785
|
error = daemonProcessException(`A server instance had not been fully shut down. Please try running the command again.`);
|
|
767
786
|
}
|
|
@@ -782,9 +801,9 @@ class DaemonClient {
|
|
|
782
801
|
this._daemonStatus = DaemonStatus.CONNECTING;
|
|
783
802
|
let serverAvailable;
|
|
784
803
|
try {
|
|
785
|
-
serverAvailable = await this.waitForServerToBeAvailable({
|
|
804
|
+
({ available: serverAvailable } = await this.waitForServerToBeAvailable({
|
|
786
805
|
ignoreVersionMismatch: false,
|
|
787
|
-
});
|
|
806
|
+
}));
|
|
788
807
|
}
|
|
789
808
|
catch (err) {
|
|
790
809
|
if (err instanceof daemon_socket_messenger_1.VersionMismatchError) {
|
|
@@ -830,6 +849,11 @@ class DaemonClient {
|
|
|
830
849
|
*/
|
|
831
850
|
async waitForServerToBeAvailable(options) {
|
|
832
851
|
logger_1.clientLogger.log(`[Client] Waiting for server (max: ${WAIT_FOR_SERVER_CONFIG.maxAttempts} attempts, ${WAIT_FOR_SERVER_CONFIG.delayMs}ms interval)`);
|
|
852
|
+
// Poll-scoped, not instance state: reconnect paths have their own flags, so
|
|
853
|
+
// several attempts can be in flight and a shared slot would let one report
|
|
854
|
+
// another's socket.
|
|
855
|
+
let stoppedOnRefusal = false;
|
|
856
|
+
let refusal;
|
|
833
857
|
const socket = await (0, wait_for_socket_connection_1.waitForSocketConnection)(() => {
|
|
834
858
|
try {
|
|
835
859
|
return this.getSocketPath();
|
|
@@ -846,14 +870,29 @@ class DaemonClient {
|
|
|
846
870
|
}, {
|
|
847
871
|
maxAttempts: WAIT_FOR_SERVER_CONFIG.maxAttempts,
|
|
848
872
|
delayMs: WAIT_FOR_SERVER_CONFIG.delayMs,
|
|
873
|
+
onConnectError: (error, socketPath) => {
|
|
874
|
+
refusal = { error, socketPath };
|
|
875
|
+
// A refusal is not expected to become an acceptance, so polling the
|
|
876
|
+
// full 60s budget only delays the same answer. Not absolute:
|
|
877
|
+
// `server.ts` binds before it chmods to 0600, so under a umask that
|
|
878
|
+
// strips owner write a same-user connect can lose a microsecond race
|
|
879
|
+
// and see EACCES. That costs a specific error rather than a retry — a
|
|
880
|
+
// better trade than a 60s hang.
|
|
881
|
+
return (stoppedOnRefusal = isPermissionErrno(error));
|
|
882
|
+
},
|
|
849
883
|
});
|
|
850
884
|
if (socket) {
|
|
851
885
|
socket.destroy();
|
|
852
886
|
logger_1.clientLogger.log(`[Client] Server available`);
|
|
853
|
-
return true;
|
|
887
|
+
return { available: true };
|
|
854
888
|
}
|
|
855
|
-
|
|
856
|
-
|
|
889
|
+
// Keyed on the early exit taken, not on whether an errno was recorded:
|
|
890
|
+
// every failed connect records one, including an ordinary cold start's
|
|
891
|
+
// ENOENT.
|
|
892
|
+
logger_1.clientLogger.log(stoppedOnRefusal
|
|
893
|
+
? `[Client] Server refused the connection (${refusal?.error.code}), stopped polling`
|
|
894
|
+
: `[Client] Server not available after ${WAIT_FOR_SERVER_CONFIG.maxAttempts} attempts`);
|
|
895
|
+
return { available: false, refusal };
|
|
857
896
|
}
|
|
858
897
|
async sendMessageToDaemon(message, force) {
|
|
859
898
|
// the first message sent to the daemon includes an env prop
|
|
@@ -935,7 +974,12 @@ class DaemonClient {
|
|
|
935
974
|
].join('\n')));
|
|
936
975
|
}
|
|
937
976
|
}
|
|
938
|
-
|
|
977
|
+
/**
|
|
978
|
+
* @param probeRefusal what the caller's pre-start probe saw. The only evidence
|
|
979
|
+
* when a daemon refuses us and then exits — the poll cannot reproduce it
|
|
980
|
+
* once the process json is gone. `nx daemon --start` passes nothing.
|
|
981
|
+
*/
|
|
982
|
+
async startInBackground(probeRefusal) {
|
|
939
983
|
if (global.NX_PLUGIN_WORKER) {
|
|
940
984
|
throw new Error('Fatal Error: Something unexpected has occurred. Plugin Workers should not start a new daemon process. Please report this issue.');
|
|
941
985
|
}
|
|
@@ -973,14 +1017,25 @@ class DaemonClient {
|
|
|
973
1017
|
/**
|
|
974
1018
|
* Ensure the server is actually available to connect to via IPC before resolving
|
|
975
1019
|
*/
|
|
976
|
-
const
|
|
977
|
-
|
|
978
|
-
});
|
|
979
|
-
if (serverAvailable) {
|
|
1020
|
+
const { available, refusal: polled } = await this.waitForServerToBeAvailable({ ignoreVersionMismatch: true });
|
|
1021
|
+
if (available) {
|
|
980
1022
|
logger_1.clientLogger.log(`[Client] Daemon server started, pid=${backgroundProcess.pid}`);
|
|
981
1023
|
return backgroundProcess.pid;
|
|
982
1024
|
}
|
|
983
1025
|
else {
|
|
1026
|
+
// A permission refusal from either source wins, then the poll's errno,
|
|
1027
|
+
// then the probe's. Recency alone would report a daemon's ENOENT over the
|
|
1028
|
+
// EACCES the probe saw a moment earlier, losing the diagnosis.
|
|
1029
|
+
const refusal = [polled, probeRefusal].find((r) => r && isPermissionErrno(r.error)) ??
|
|
1030
|
+
polled ??
|
|
1031
|
+
probeRefusal;
|
|
1032
|
+
if (refusal && isPermissionErrno(refusal.error)) {
|
|
1033
|
+
// Reported here rather than as a generic startup failure, so it degrades
|
|
1034
|
+
// without disabling the daemon until `nx reset`. Both operands come from
|
|
1035
|
+
// the refusal: resolving a path here instead would throw once a daemon
|
|
1036
|
+
// that failed to bind has unlinked its process json.
|
|
1037
|
+
throw daemonPermissionException(refusal.socketPath, refusal.error.message);
|
|
1038
|
+
}
|
|
984
1039
|
throw daemonProcessException('Failed to start or connect to the Nx Daemon process.');
|
|
985
1040
|
}
|
|
986
1041
|
}
|
|
@@ -1023,24 +1078,65 @@ function isDocker() {
|
|
|
1023
1078
|
function nxJsonIsNotPresent() {
|
|
1024
1079
|
return !(0, nx_json_1.hasNxJson)(workspace_root_1.workspaceRoot);
|
|
1025
1080
|
}
|
|
1081
|
+
/**
|
|
1082
|
+
* EACCES and EPERM are the two errnos that mean the OS refused us rather than
|
|
1083
|
+
* that nothing was listening. They need opposite remedies — a socket owned by
|
|
1084
|
+
* someone else versus a sandbox refusing the connect syscall — but they share
|
|
1085
|
+
* the property that retrying cannot change the answer.
|
|
1086
|
+
*/
|
|
1087
|
+
function isPermissionErrno(error) {
|
|
1088
|
+
return error?.code === 'EACCES' || error?.code === 'EPERM';
|
|
1089
|
+
}
|
|
1090
|
+
/**
|
|
1091
|
+
* The operating system refused the connection. Most often the socket belongs to
|
|
1092
|
+
* another user, which is the guarantee the owner-only socket directory buys —
|
|
1093
|
+
* but a sandbox that denies unix-socket connects produces the same errno, so the
|
|
1094
|
+
* message does not assert which. Either way it is an environment condition
|
|
1095
|
+
* rather than a defect in Nx, and it deliberately does not
|
|
1096
|
+
* carry `internalDaemonError`: that tag tells the user to file an issue and
|
|
1097
|
+
* disables the daemon until `nx reset`, which would outlast the stale socket
|
|
1098
|
+
* that caused it.
|
|
1099
|
+
*
|
|
1100
|
+
* It also skips the daemon log that `daemonProcessException` appends. The log
|
|
1101
|
+
* belongs to *our* daemon; the process holding this socket is someone else's, so
|
|
1102
|
+
* quoting it would describe an unrelated run.
|
|
1103
|
+
*/
|
|
1104
|
+
function daemonPermissionException(socketPath, cause) {
|
|
1105
|
+
const error = new Error([
|
|
1106
|
+
`The operating system refused the connection to the Nx Daemon socket (${cause}).`,
|
|
1107
|
+
'',
|
|
1108
|
+
`Socket: ${socketPath}`,
|
|
1109
|
+
'',
|
|
1110
|
+
'Most often the socket belongs to a different user: a daemon left behind by running Nx under `sudo`, a different uid inside a container, or a working copy shared between accounts. If the socket is your own, a sandbox is refusing the connection instead.',
|
|
1111
|
+
'If it belongs to another user, delete the socket above or set NX_SOCKET_DIR to a directory only your user can reach. If you are in a sandbox, allow unix sockets under the Nx socket root — in Claude Code a scoped `allowUnixSockets` only permits connecting, so starting a daemon there needs `allowAllUnixSockets: true`. See https://nx.dev/docs/kb/nx-sandbox-unix-sockets',
|
|
1112
|
+
].join('\n'));
|
|
1113
|
+
error.daemonPermissionError = true;
|
|
1114
|
+
return error;
|
|
1115
|
+
}
|
|
1116
|
+
/**
|
|
1117
|
+
* Exported for testing: the `internalDaemonError` tag decides whether a daemon
|
|
1118
|
+
* failure degrades to a daemonless graph build or aborts the command.
|
|
1119
|
+
*/
|
|
1026
1120
|
function daemonProcessException(message) {
|
|
1121
|
+
// The log is an enrichment, not the classifier: it is absent on a first run,
|
|
1122
|
+
// which is exactly when the daemon is most likely to fail to start.
|
|
1123
|
+
let body = message;
|
|
1027
1124
|
try {
|
|
1028
1125
|
let log = (0, node_fs_1.readFileSync)(tmp_dir_1.DAEMON_OUTPUT_LOG_FILE).toString().split('\n');
|
|
1029
1126
|
if (log.length > 20) {
|
|
1030
1127
|
log = log.slice(log.length - 20);
|
|
1031
1128
|
}
|
|
1032
|
-
|
|
1129
|
+
body = [
|
|
1033
1130
|
message,
|
|
1034
1131
|
'',
|
|
1035
1132
|
'Messages from the log:',
|
|
1036
1133
|
...log,
|
|
1037
1134
|
'\n',
|
|
1038
1135
|
`More information: ${tmp_dir_1.DAEMON_OUTPUT_LOG_FILE}`,
|
|
1039
|
-
].join('\n')
|
|
1040
|
-
error.internalDaemonError = true;
|
|
1041
|
-
return error;
|
|
1042
|
-
}
|
|
1043
|
-
catch (e) {
|
|
1044
|
-
return new Error(message);
|
|
1136
|
+
].join('\n');
|
|
1045
1137
|
}
|
|
1138
|
+
catch { }
|
|
1139
|
+
const error = new Error(body);
|
|
1140
|
+
error.internalDaemonError = true;
|
|
1141
|
+
return error;
|
|
1046
1142
|
}
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.DaemonSocketMessenger = exports.VersionMismatchError = void 0;
|
|
4
4
|
const perf_hooks_1 = require("perf_hooks");
|
|
5
5
|
const consume_messages_from_socket_1 = require("../../utils/consume-messages-from-socket");
|
|
6
|
+
const workspace_root_1 = require("../../utils/workspace-root");
|
|
6
7
|
const logger_1 = require("../logger");
|
|
7
8
|
const socket_utils_1 = require("../socket-utils");
|
|
8
9
|
class VersionMismatchError extends Error {
|
|
@@ -21,6 +22,9 @@ class DaemonSocketMessenger {
|
|
|
21
22
|
if (!this.socket) {
|
|
22
23
|
throw new Error('Socket not initialized.');
|
|
23
24
|
}
|
|
25
|
+
// Stamp every message with the sending workspace's root so the daemon can
|
|
26
|
+
// reject messages from a different workspace (e.g. a shared NX_SOCKET_DIR).
|
|
27
|
+
messageToDaemon.workspaceRoot = workspace_root_1.workspaceRoot;
|
|
24
28
|
logger_1.clientLogger.log('[Messenger] Sending message type:', messageToDaemon.type);
|
|
25
29
|
perf_hooks_1.performance.mark('daemon-message-serialization-start-' + messageToDaemon.type);
|
|
26
30
|
const serialized = (0, socket_utils_1.serialize)(messageToDaemon, force);
|
|
@@ -1,6 +1,38 @@
|
|
|
1
1
|
export type DaemonMessage = {
|
|
2
2
|
type: string;
|
|
3
3
|
env?: Record<string, string>;
|
|
4
|
+
/**
|
|
5
|
+
* Stamped centrally by `DaemonSocketMessenger.sendMessage`, not by the ~30
|
|
6
|
+
* sites that construct messages — hence optional. It can also be absent on a
|
|
7
|
+
* message that predates the field, which is why `isForeignWorkspaceMessage`
|
|
8
|
+
* treats `undefined` as "not foreign".
|
|
9
|
+
*/
|
|
10
|
+
workspaceRoot?: string;
|
|
4
11
|
data?: any;
|
|
5
12
|
};
|
|
6
13
|
export declare function isDaemonMessage(msg: unknown): msg is DaemonMessage;
|
|
14
|
+
/**
|
|
15
|
+
* Any message carrying the sender's workspace root. Structural so the checks
|
|
16
|
+
* below serve the daemon and the plugin worker without either importing the
|
|
17
|
+
* other's message type. `type` is required only so the type is not all-optional,
|
|
18
|
+
* which would accept `{}`.
|
|
19
|
+
*/
|
|
20
|
+
type WorkspaceScopedMessage = {
|
|
21
|
+
type: string;
|
|
22
|
+
workspaceRoot?: string;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* A message from a different workspace — two sharing an NX_SOCKET_DIR — must not
|
|
26
|
+
* be processed. Compared directly; both come from the same resolution.
|
|
27
|
+
*/
|
|
28
|
+
export declare function isForeignWorkspaceMessage(msg: WorkspaceScopedMessage, receiverWorkspaceRoot: string): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Throws on a message this receiver must not act on. The daemon catches it and
|
|
31
|
+
* responds with the mismatch; the plugin worker catches it and drops the
|
|
32
|
+
* message. `receiverDescription` names whichever raised it.
|
|
33
|
+
*
|
|
34
|
+
* An unstamped message is deliberately accepted: this is accident detection,
|
|
35
|
+
* not a control.
|
|
36
|
+
*/
|
|
37
|
+
export declare function assertNotForeignWorkspaceMessage(msg: WorkspaceScopedMessage, receiverWorkspaceRoot: string, receiverDescription?: string): void;
|
|
38
|
+
export {};
|
|
@@ -1,6 +1,31 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.isDaemonMessage = isDaemonMessage;
|
|
4
|
+
exports.isForeignWorkspaceMessage = isForeignWorkspaceMessage;
|
|
5
|
+
exports.assertNotForeignWorkspaceMessage = assertNotForeignWorkspaceMessage;
|
|
4
6
|
function isDaemonMessage(msg) {
|
|
5
7
|
return typeof msg === 'object' && msg && 'type' in msg;
|
|
6
8
|
}
|
|
9
|
+
/**
|
|
10
|
+
* A message from a different workspace — two sharing an NX_SOCKET_DIR — must not
|
|
11
|
+
* be processed. Compared directly; both come from the same resolution.
|
|
12
|
+
*/
|
|
13
|
+
function isForeignWorkspaceMessage(msg, receiverWorkspaceRoot) {
|
|
14
|
+
if (msg.workspaceRoot === undefined) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
return msg.workspaceRoot !== receiverWorkspaceRoot;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Throws on a message this receiver must not act on. The daemon catches it and
|
|
21
|
+
* responds with the mismatch; the plugin worker catches it and drops the
|
|
22
|
+
* message. `receiverDescription` names whichever raised it.
|
|
23
|
+
*
|
|
24
|
+
* An unstamped message is deliberately accepted: this is accident detection,
|
|
25
|
+
* not a control.
|
|
26
|
+
*/
|
|
27
|
+
function assertNotForeignWorkspaceMessage(msg, receiverWorkspaceRoot, receiverDescription = `The Nx Daemon for '${receiverWorkspaceRoot}'`) {
|
|
28
|
+
if (isForeignWorkspaceMessage(msg, receiverWorkspaceRoot)) {
|
|
29
|
+
throw new Error(`${receiverDescription} received a message from a different workspace ('${msg.workspaceRoot}') and refused to process it. This usually means multiple workspaces are sharing a socket directory; ensure NX_SOCKET_DIR (or NX_DAEMON_SOCKET_DIR) is not set to a shared location.`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getLatestNxTmpPath = getLatestNxTmpPath;
|
|
4
4
|
exports.cleanupLatestNx = cleanupLatestNx;
|
|
5
|
-
|
|
5
|
+
// Import from the defining module, not the devkit-internals barrel: the barrel
|
|
6
|
+
// eagerly pulls in the task-execution subsystem and instantiates a daemon
|
|
7
|
+
// client, all of which would stay resident for the daemon server's lifetime.
|
|
8
|
+
const package_json_1 = require("../../utils/package-json");
|
|
6
9
|
const package_manager_1 = require("../../utils/package-manager");
|
|
7
10
|
const provenance_1 = require("../../utils/provenance");
|
|
8
11
|
const workspace_root_1 = require("../../utils/workspace-root");
|
|
@@ -28,7 +31,7 @@ async function getLatestNxTmpPath() {
|
|
|
28
31
|
try {
|
|
29
32
|
logger_1.serverLogger.log('[LATEST-NX]: Pulling latest Nx...');
|
|
30
33
|
await (0, provenance_1.ensurePackageHasProvenance)('nx', 'latest');
|
|
31
|
-
const result = await (0,
|
|
34
|
+
const result = await (0, package_json_1.installPackageToTmpAsync)('nx', 'latest', (0, package_manager_1.detectPackageManager)(workspace_root_1.workspaceRoot));
|
|
32
35
|
latestNxTmpPath = result.tempDir;
|
|
33
36
|
cleanupFn = result.cleanup;
|
|
34
37
|
logger_1.serverLogger.log('[LATEST-NX]: Successfully pulled latest Nx to', latestNxTmpPath);
|
|
@@ -119,6 +119,17 @@ async function handleMessage(socket, data) {
|
|
|
119
119
|
await (0, shutdown_utils_1.respondWithErrorAndExit)(socket, `Invalid payload from the client`, new Error(`Unsupported payload sent to daemon server: ${unparsedPayload}`));
|
|
120
120
|
}
|
|
121
121
|
logger_1.serverLogger.log(`Received ${mode} message of type ${payload.type}`);
|
|
122
|
+
// A mismatch means the client reached the wrong daemon (e.g. a shared
|
|
123
|
+
// NX_SOCKET_DIR). Respond, but stay alive for our own workspace.
|
|
124
|
+
if ((0, daemon_message_1.isDaemonMessage)(payload)) {
|
|
125
|
+
try {
|
|
126
|
+
(0, daemon_message_1.assertNotForeignWorkspaceMessage)(payload, workspace_root_1.workspaceRoot);
|
|
127
|
+
}
|
|
128
|
+
catch (e) {
|
|
129
|
+
await (0, shutdown_utils_1.respondWithError)(socket, `Workspace root mismatch`, e);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
122
133
|
if ((0, daemon_message_1.isDaemonMessage)(payload) && payload.env) {
|
|
123
134
|
const changedEnvKeys = (0, daemon_environment_1.applyDaemonEnvFromClient)(payload.env);
|
|
124
135
|
if (changedEnvKeys.length > 0) {
|
|
@@ -423,6 +434,17 @@ async function startServer() {
|
|
|
423
434
|
server.listen(socketPath, async () => {
|
|
424
435
|
try {
|
|
425
436
|
logger_1.serverLogger.log(`Started listening on: ${socketPath}`);
|
|
437
|
+
// Linux gates connect() on write permission to the socket file; macOS/BSD gate
|
|
438
|
+
// on the directory, which is already 0700. Done after listen because
|
|
439
|
+
// net.Server.listen takes no mode and umask is process-global.
|
|
440
|
+
if (!socket_utils_1.isWindows) {
|
|
441
|
+
try {
|
|
442
|
+
(0, fs_1.chmodSync)(socketPath, 0o600);
|
|
443
|
+
}
|
|
444
|
+
catch {
|
|
445
|
+
// Best effort; the 0700 socket directory is the primary control.
|
|
446
|
+
}
|
|
447
|
+
}
|
|
426
448
|
// this triggers the storage of the lock file hash
|
|
427
449
|
daemonIsOutdated();
|
|
428
450
|
if (!(0, shutdown_utils_1.getOutputWatcherInstance)()) {
|
|
@@ -14,5 +14,10 @@ export declare function handleServerProcessTermination({ server, reason, sockets
|
|
|
14
14
|
export declare function handleServerProcessTerminationWithRestart({ server, reason, sockets, }: HandleServerProcessTerminationParams): Promise<void>;
|
|
15
15
|
export declare function resetInactivityTimeout(cb: () => void): void;
|
|
16
16
|
export declare function respondToClient(socket: Socket, response: string, description: string): Promise<unknown>;
|
|
17
|
+
/**
|
|
18
|
+
* Send an error to the client without terminating the daemon. Use this when the
|
|
19
|
+
* daemon should keep running for other clients after refusing a request.
|
|
20
|
+
*/
|
|
21
|
+
export declare function respondWithError(socket: Socket, description: string, error: Error): Promise<void>;
|
|
17
22
|
export declare function respondWithErrorAndExit(socket: Socket, description: string, error: Error): Promise<void>;
|
|
18
23
|
export {};
|
|
@@ -9,6 +9,7 @@ exports.handleServerProcessTermination = handleServerProcessTermination;
|
|
|
9
9
|
exports.handleServerProcessTerminationWithRestart = handleServerProcessTerminationWithRestart;
|
|
10
10
|
exports.resetInactivityTimeout = resetInactivityTimeout;
|
|
11
11
|
exports.respondToClient = respondToClient;
|
|
12
|
+
exports.respondWithError = respondWithError;
|
|
12
13
|
exports.respondWithErrorAndExit = respondWithErrorAndExit;
|
|
13
14
|
const workspace_root_1 = require("../../utils/workspace-root");
|
|
14
15
|
const logger_1 = require("../logger");
|
|
@@ -135,9 +136,12 @@ function respondToClient(socket, response, description) {
|
|
|
135
136
|
});
|
|
136
137
|
});
|
|
137
138
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
139
|
+
/**
|
|
140
|
+
* Send an error to the client without terminating the daemon. Use this when the
|
|
141
|
+
* daemon should keep running for other clients after refusing a request.
|
|
142
|
+
*/
|
|
143
|
+
async function respondWithError(socket, description, error) {
|
|
144
|
+
const normalizedError = error instanceof error_types_1.DaemonProjectGraphError
|
|
141
145
|
? error_types_1.ProjectGraphError.fromDaemonProjectGraphError(error)
|
|
142
146
|
: error;
|
|
143
147
|
// print some extra stuff in the error message
|
|
@@ -145,8 +149,11 @@ async function respondWithErrorAndExit(socket, description, error) {
|
|
|
145
149
|
console.error(normalizedError.stack);
|
|
146
150
|
// Respond with the original error
|
|
147
151
|
await respondToClient(socket, (0, socket_utils_1.serializeResult)(error, null, null), null);
|
|
152
|
+
}
|
|
153
|
+
async function respondWithErrorAndExit(socket, description, error) {
|
|
154
|
+
await respondWithError(socket, description, error);
|
|
148
155
|
// Project Graph errors are okay. Restarting the daemon won't help with this.
|
|
149
|
-
if (!
|
|
156
|
+
if (!(error instanceof error_types_1.DaemonProjectGraphError)) {
|
|
150
157
|
process.exit(1);
|
|
151
158
|
}
|
|
152
159
|
}
|
|
@@ -8,6 +8,7 @@ export declare const isWindows: boolean;
|
|
|
8
8
|
export declare const getFullOsSocketPath: () => string;
|
|
9
9
|
export declare const getForkedProcessOsSocketPath: (id: string) => string;
|
|
10
10
|
export declare const getPluginOsSocketPath: (id: string) => string;
|
|
11
|
+
export declare function getPluginSocketFileName(id: string): string;
|
|
11
12
|
export declare function killSocketOrPath(): void;
|
|
12
13
|
export declare function serializeResult(error: Error | null, serializedProjectGraph: string | null, serializedSourceMaps: string | null): string | null;
|
|
13
14
|
/**
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getPluginOsSocketPath = exports.getForkedProcessOsSocketPath = exports.getFullOsSocketPath = exports.isWindows = void 0;
|
|
4
|
+
exports.getPluginSocketFileName = getPluginSocketFileName;
|
|
4
5
|
exports.killSocketOrPath = killSocketOrPath;
|
|
5
6
|
exports.serializeResult = serializeResult;
|
|
6
7
|
exports.serialize = serialize;
|
|
@@ -31,18 +32,38 @@ const getForkedProcessOsSocketPath = (id) => {
|
|
|
31
32
|
};
|
|
32
33
|
exports.getForkedProcessOsSocketPath = getForkedProcessOsSocketPath;
|
|
33
34
|
const getPluginOsSocketPath = (id) => {
|
|
34
|
-
let path = (0, path_1.resolve)((0, path_1.join)((0, tmp_dir_1.
|
|
35
|
+
let path = (0, path_1.resolve)((0, path_1.join)((0, tmp_dir_1.getPluginSocketDir)(), getPluginSocketFileName(id)));
|
|
35
36
|
assertValidSocketPath(path);
|
|
36
37
|
return exports.isWindows ? '\\\\.\\pipe\\nx\\' + path : path;
|
|
37
38
|
};
|
|
38
39
|
exports.getPluginOsSocketPath = getPluginOsSocketPath;
|
|
40
|
+
function getPluginSocketFileName(id) {
|
|
41
|
+
return `p${id}.sock`;
|
|
42
|
+
}
|
|
39
43
|
function assertValidSocketPath(path) {
|
|
40
44
|
if (path.length > 95) {
|
|
45
|
+
const fallbackCause = (0, tmp_dir_1.getSocketDirFallbackCause)();
|
|
46
|
+
const refusedConfiguredSocketDir = (0, tmp_dir_1.getRefusedConfiguredSocketDir)();
|
|
41
47
|
throw new Error([
|
|
42
48
|
'Attempted to open socket that exceeds the maximum socket length.',
|
|
49
|
+
...(fallbackCause === undefined
|
|
50
|
+
? []
|
|
51
|
+
: [
|
|
52
|
+
`Nx fell back to ${(0, path_1.dirname)(path)} because the default socket directory could not be used.`,
|
|
53
|
+
'Run the command with --verbose to see why the default directory was rejected.',
|
|
54
|
+
]),
|
|
43
55
|
'',
|
|
44
|
-
|
|
45
|
-
|
|
56
|
+
...(refusedConfiguredSocketDir === undefined
|
|
57
|
+
? [
|
|
58
|
+
`Set NX_SOCKET_DIR to a shorter path (e.g. ${exports.isWindows ? '%TMP%/nx-tmp' : '/tmp/nx-tmp'}) to avoid this issue.`,
|
|
59
|
+
]
|
|
60
|
+
: [
|
|
61
|
+
// Saying "set a shorter path" here would be advice they already
|
|
62
|
+
// followed: they set one, and it was refused for another reason.
|
|
63
|
+
`The directory set in NX_SOCKET_DIR (${refusedConfiguredSocketDir}) could not be used — see the warning above — so Nx fell back to a longer path.`,
|
|
64
|
+
'Point NX_SOCKET_DIR at a short directory your user owns.',
|
|
65
|
+
]),
|
|
66
|
+
].join('\n'), fallbackCause === undefined ? undefined : { cause: fallbackCause });
|
|
46
67
|
}
|
|
47
68
|
}
|
|
48
69
|
function killSocketOrPath() {
|