u-foo 3.0.21 → 3.0.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -7
- package/README.zh-CN.md +11 -7
- package/dist/tui/darwin-arm64/ufoo-tui +0 -0
- package/dist/tui/darwin-x64/ufoo-tui +0 -0
- package/dist/tui/linux-arm64/ufoo-tui +0 -0
- package/dist/tui/linux-x64/ufoo-tui +0 -0
- package/package.json +1 -1
- package/src/agents/launch/launcher.js +52 -29
- package/src/app/chat/commandExecutor.js +15 -0
- package/src/app/chat/daemonConnection.js +7 -2
- package/src/app/chat/daemonCoordinator.js +1 -0
- package/src/app/chat/daemonTransport.js +21 -5
- package/src/app/chat/projectCloseController.js +6 -1
- package/src/app/chat/transport.js +84 -8
- package/src/app/cli/run.js +44 -7
- package/src/config.js +48 -0
- package/src/runtime/contracts/eventContract.js +1 -0
- package/src/runtime/daemon/agentProcessManager.js +34 -14
- package/src/runtime/daemon/controlPlaneService.js +14 -5
- package/src/runtime/daemon/endpoint.js +79 -0
- package/src/runtime/daemon/globalDaemon.js +394 -0
- package/src/runtime/daemon/groupOrchestrator.js +6 -0
- package/src/runtime/daemon/index.js +272 -109
- package/src/runtime/daemon/mcpHttpServer.js +5 -0
- package/src/runtime/daemon/ops.js +48 -17
- package/src/runtime/daemon/processLifecycle.js +127 -0
- package/src/runtime/daemon/projectContext.js +46 -0
- package/src/runtime/daemon/projectRuntime.js +221 -0
- package/src/runtime/daemon/projectRuntimeGateway.js +72 -0
- package/src/runtime/daemon/projectRuntimeManager.js +268 -0
- package/src/runtime/daemon/run.js +66 -12
- package/src/runtime/projects/registry.js +26 -4
- package/src/runtime/projects/runtimes.js +1 -1
- package/src/ui/rustChatHost.js +46 -8
package/src/app/cli/run.js
CHANGED
|
@@ -3,7 +3,11 @@ const os = require("os");
|
|
|
3
3
|
const { spawnSync } = require("child_process");
|
|
4
4
|
const net = require("net");
|
|
5
5
|
const fs = require("fs");
|
|
6
|
-
const {
|
|
6
|
+
const { isRunning } = require("../../runtime/daemon");
|
|
7
|
+
const {
|
|
8
|
+
resolveDaemonEndpoint,
|
|
9
|
+
routeDaemonRequest,
|
|
10
|
+
} = require("../../runtime/daemon/endpoint");
|
|
7
11
|
const { runBusCoreCommand } = require("./busCoreCommands");
|
|
8
12
|
const { runCtxCommand } = require("./ctxCoreCommands");
|
|
9
13
|
const { runOnlineCommand } = require("./onlineCoreCommands");
|
|
@@ -59,10 +63,24 @@ async function connectWithRetry(sockPath, retries, delayMs) {
|
|
|
59
63
|
}
|
|
60
64
|
|
|
61
65
|
async function ensureDaemonRunning(projectRoot) {
|
|
62
|
-
|
|
66
|
+
const endpoint = resolveDaemonEndpoint(projectRoot);
|
|
67
|
+
const daemonRoot = endpoint.scope === "global"
|
|
68
|
+
? endpoint.controllerRoot
|
|
69
|
+
: endpoint.projectRoot;
|
|
70
|
+
if (isRunning(daemonRoot)) return;
|
|
63
71
|
const repoRoot = getPackageRoot();
|
|
64
|
-
run(
|
|
65
|
-
|
|
72
|
+
run(
|
|
73
|
+
resolveNodeExecutable(),
|
|
74
|
+
[path.join(repoRoot, "bin", "ufoo.js"), "daemon", "start"],
|
|
75
|
+
{
|
|
76
|
+
cwd: daemonRoot,
|
|
77
|
+
env: {
|
|
78
|
+
...process.env,
|
|
79
|
+
UFOO_DAEMON_TOPOLOGY: endpoint.topology,
|
|
80
|
+
},
|
|
81
|
+
}
|
|
82
|
+
);
|
|
83
|
+
const sock = endpoint.socketPath;
|
|
66
84
|
for (let i = 0; i < 30; i += 1) {
|
|
67
85
|
if (fs.existsSync(sock)) {
|
|
68
86
|
return;
|
|
@@ -74,7 +92,8 @@ async function ensureDaemonRunning(projectRoot) {
|
|
|
74
92
|
}
|
|
75
93
|
|
|
76
94
|
async function sendDaemonRequest(projectRoot, payload) {
|
|
77
|
-
const
|
|
95
|
+
const endpoint = resolveDaemonEndpoint(projectRoot);
|
|
96
|
+
const sock = endpoint.socketPath;
|
|
78
97
|
const client = await connectWithRetry(sock, 25, 200);
|
|
79
98
|
if (!client) {
|
|
80
99
|
throw new Error("Failed to connect to ufoo daemon");
|
|
@@ -125,7 +144,7 @@ async function sendDaemonRequest(projectRoot, payload) {
|
|
|
125
144
|
cleanup();
|
|
126
145
|
reject(err);
|
|
127
146
|
});
|
|
128
|
-
client.write(`${JSON.stringify(payload)}\n`);
|
|
147
|
+
client.write(`${JSON.stringify(routeDaemonRequest(endpoint, payload))}\n`);
|
|
129
148
|
});
|
|
130
149
|
}
|
|
131
150
|
|
|
@@ -186,6 +205,17 @@ function collectHostLaunchRequestContext(env = process.env) {
|
|
|
186
205
|
return context;
|
|
187
206
|
}
|
|
188
207
|
|
|
208
|
+
function collectTmuxLaunchRequestContext(env = process.env) {
|
|
209
|
+
const tmuxTarget = String(env.UFOO_TMUX_TARGET || "").trim();
|
|
210
|
+
const tmuxPane = String(env.UFOO_TMUX_PANE || env.TMUX_PANE || "").trim();
|
|
211
|
+
const tmuxSession = String(env.UFOO_TMUX_SESSION || "").trim();
|
|
212
|
+
const context = {};
|
|
213
|
+
if (tmuxTarget) context.tmux_target = tmuxTarget;
|
|
214
|
+
if (tmuxPane) context.tmux_pane = tmuxPane;
|
|
215
|
+
if (tmuxSession) context.tmux_session = tmuxSession;
|
|
216
|
+
return context;
|
|
217
|
+
}
|
|
218
|
+
|
|
189
219
|
function collectOption(value, previous) {
|
|
190
220
|
const next = Array.isArray(previous) ? previous.slice() : [];
|
|
191
221
|
const parts = String(value || "")
|
|
@@ -567,10 +597,12 @@ async function runCli(argv) {
|
|
|
567
597
|
.option("--start", "Start daemon")
|
|
568
598
|
.option("--stop", "Stop daemon")
|
|
569
599
|
.option("--status", "Check daemon status")
|
|
600
|
+
.option("--topology <mode>", "Set machine-wide daemon topology: project, hybrid, or global")
|
|
570
601
|
.action((opts) => {
|
|
571
602
|
const repoRoot = getPackageRoot();
|
|
572
603
|
const args = ["daemon"];
|
|
573
|
-
if (opts.
|
|
604
|
+
if (opts.topology) args.push("topology", opts.topology);
|
|
605
|
+
else if (opts.start) args.push("start");
|
|
574
606
|
else if (opts.stop) args.push("stop");
|
|
575
607
|
else if (opts.status) args.push("status");
|
|
576
608
|
run(process.execPath, [path.join(repoRoot, "bin", "ufoo.js"), ...args]);
|
|
@@ -769,6 +801,7 @@ async function runCli(argv) {
|
|
|
769
801
|
prompt_profile: opts.profile || "",
|
|
770
802
|
count: 1,
|
|
771
803
|
...collectHostLaunchRequestContext(),
|
|
804
|
+
...collectTmuxLaunchRequestContext(),
|
|
772
805
|
});
|
|
773
806
|
const reply = resp?.data?.reply || `Launching ${normalizedAgent} agent...`;
|
|
774
807
|
console.log(reply);
|
|
@@ -832,6 +865,7 @@ async function runCli(argv) {
|
|
|
832
865
|
count: 1,
|
|
833
866
|
launch_scope: scope,
|
|
834
867
|
...collectHostLaunchRequestContext(),
|
|
868
|
+
...collectTmuxLaunchRequestContext(),
|
|
835
869
|
});
|
|
836
870
|
console.log(resp?.data?.reply || `Launched ${agent} role ${promptProfile}`);
|
|
837
871
|
} catch (err) {
|
|
@@ -1200,6 +1234,7 @@ async function runCli(argv) {
|
|
|
1200
1234
|
instance: opts.instance || "",
|
|
1201
1235
|
dry_run: opts.dryRun === true,
|
|
1202
1236
|
...collectHostLaunchRequestContext(),
|
|
1237
|
+
...collectTmuxLaunchRequestContext(),
|
|
1203
1238
|
});
|
|
1204
1239
|
if (opts.json) {
|
|
1205
1240
|
console.log(JSON.stringify(resp?.data || {}, null, 2));
|
|
@@ -2175,6 +2210,7 @@ async function runCli(argv) {
|
|
|
2175
2210
|
instance,
|
|
2176
2211
|
dry_run: dryRun,
|
|
2177
2212
|
...collectHostLaunchRequestContext(),
|
|
2213
|
+
...collectTmuxLaunchRequestContext(),
|
|
2178
2214
|
});
|
|
2179
2215
|
if (outputJson) {
|
|
2180
2216
|
console.log(JSON.stringify(resp?.data || {}, null, 2));
|
|
@@ -2311,6 +2347,7 @@ async function runCli(argv) {
|
|
|
2311
2347
|
count: 1,
|
|
2312
2348
|
launch_scope: scope,
|
|
2313
2349
|
...collectHostLaunchRequestContext(),
|
|
2350
|
+
...collectTmuxLaunchRequestContext(),
|
|
2314
2351
|
});
|
|
2315
2352
|
console.log(resp?.data?.reply || `Launched ${agent} role ${profile}`);
|
|
2316
2353
|
return;
|
package/src/config.js
CHANGED
|
@@ -34,6 +34,7 @@ const SETTINGS_MODEL_DEFAULTS = Object.freeze({
|
|
|
34
34
|
|
|
35
35
|
const DEFAULT_CONFIG = {
|
|
36
36
|
launchMode: "auto",
|
|
37
|
+
daemonTopology: "global",
|
|
37
38
|
agentProvider: "codex-cli",
|
|
38
39
|
controllerMode: "main",
|
|
39
40
|
mcpPort: 47631,
|
|
@@ -67,6 +68,14 @@ function normalizeLaunchMode(value) {
|
|
|
67
68
|
return "auto";
|
|
68
69
|
}
|
|
69
70
|
|
|
71
|
+
function normalizeDaemonTopology(value) {
|
|
72
|
+
const raw = String(value || "").trim().toLowerCase();
|
|
73
|
+
if (raw === "project") return "project";
|
|
74
|
+
if (raw === "hybrid") return "hybrid";
|
|
75
|
+
if (raw === "global") return "global";
|
|
76
|
+
return DEFAULT_CONFIG.daemonTopology;
|
|
77
|
+
}
|
|
78
|
+
|
|
70
79
|
function normalizeAgentProvider(value) {
|
|
71
80
|
if (value === "claude-cli") return "claude-cli";
|
|
72
81
|
if (value === "agy-cli" || value === "agy" || value === "antigravity") return "agy-cli";
|
|
@@ -175,6 +184,7 @@ function loadJsonSafe(filePath) {
|
|
|
175
184
|
function loadConfig(projectRoot) {
|
|
176
185
|
try {
|
|
177
186
|
const raw = loadJsonSafe(configPath(projectRoot));
|
|
187
|
+
const globalRaw = loadJsonSafe(globalConfigPath());
|
|
178
188
|
const agentProvider = normalizeAgentProvider(raw.agentProvider);
|
|
179
189
|
const routerProvider = normalizeModel(
|
|
180
190
|
raw.routerProvider,
|
|
@@ -184,6 +194,11 @@ function loadConfig(projectRoot) {
|
|
|
184
194
|
...DEFAULT_CONFIG,
|
|
185
195
|
...raw,
|
|
186
196
|
launchMode: normalizeLaunchMode(raw.launchMode),
|
|
197
|
+
daemonTopology: normalizeDaemonTopology(
|
|
198
|
+
Object.prototype.hasOwnProperty.call(raw, "daemonTopology")
|
|
199
|
+
? raw.daemonTopology
|
|
200
|
+
: globalRaw.daemonTopology
|
|
201
|
+
),
|
|
187
202
|
agentProvider,
|
|
188
203
|
agentModel: normalizeModel(raw.agentModel, defaultAgentModelForProvider(agentProvider)),
|
|
189
204
|
routerProvider,
|
|
@@ -238,11 +253,22 @@ function saveConfig(projectRoot, config) {
|
|
|
238
253
|
...existing,
|
|
239
254
|
...projectUpdates,
|
|
240
255
|
};
|
|
256
|
+
const hasProjectDaemonTopology =
|
|
257
|
+
Object.prototype.hasOwnProperty.call(existing, "daemonTopology")
|
|
258
|
+
|| Object.prototype.hasOwnProperty.call(projectUpdates, "daemonTopology");
|
|
241
259
|
// Remove any stale ucode fields from project config
|
|
242
260
|
for (const f of UCODE_FIELDS) {
|
|
243
261
|
delete merged[f];
|
|
244
262
|
}
|
|
245
263
|
merged.launchMode = normalizeLaunchMode(merged.launchMode);
|
|
264
|
+
if (hasProjectDaemonTopology) {
|
|
265
|
+
merged.daemonTopology = normalizeDaemonTopology(merged.daemonTopology);
|
|
266
|
+
} else {
|
|
267
|
+
// Topology is a machine-wide rollout setting unless a project explicitly
|
|
268
|
+
// opts into an override. Do not persist the default and accidentally pin
|
|
269
|
+
// this project to project-daemon mode forever.
|
|
270
|
+
delete merged.daemonTopology;
|
|
271
|
+
}
|
|
246
272
|
merged.agentProvider = normalizeAgentProvider(merged.agentProvider);
|
|
247
273
|
merged.agentModel = typeof merged.agentModel === "string" ? merged.agentModel.trim() : "";
|
|
248
274
|
merged.routerProvider = typeof merged.routerProvider === "string" ? merged.routerProvider.trim() : "";
|
|
@@ -286,13 +312,35 @@ function saveGlobalUcodeConfig(updates = {}) {
|
|
|
286
312
|
return merged;
|
|
287
313
|
}
|
|
288
314
|
|
|
315
|
+
function loadGlobalDaemonConfig() {
|
|
316
|
+
const raw = loadJsonSafe(globalConfigPath());
|
|
317
|
+
return {
|
|
318
|
+
daemonTopology: normalizeDaemonTopology(raw.daemonTopology),
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function saveGlobalDaemonConfig(updates = {}) {
|
|
323
|
+
const target = globalConfigPath();
|
|
324
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
325
|
+
const existing = loadJsonSafe(target);
|
|
326
|
+
const merged = { ...existing };
|
|
327
|
+
if (Object.prototype.hasOwnProperty.call(updates, "daemonTopology")) {
|
|
328
|
+
merged.daemonTopology = normalizeDaemonTopology(updates.daemonTopology);
|
|
329
|
+
}
|
|
330
|
+
fs.writeFileSync(target, JSON.stringify(merged, null, 2));
|
|
331
|
+
return loadGlobalDaemonConfig();
|
|
332
|
+
}
|
|
333
|
+
|
|
289
334
|
module.exports = {
|
|
290
335
|
SETTINGS_MODEL_DEFAULTS,
|
|
291
336
|
loadConfig,
|
|
292
337
|
saveConfig,
|
|
293
338
|
loadGlobalUcodeConfig,
|
|
294
339
|
saveGlobalUcodeConfig,
|
|
340
|
+
loadGlobalDaemonConfig,
|
|
341
|
+
saveGlobalDaemonConfig,
|
|
295
342
|
normalizeLaunchMode,
|
|
343
|
+
normalizeDaemonTopology,
|
|
296
344
|
normalizeAgentProvider,
|
|
297
345
|
providerKey,
|
|
298
346
|
sameModelProvider,
|
|
@@ -5,7 +5,7 @@ const EventBus = require("../../coordination/bus");
|
|
|
5
5
|
class AgentProcessManager {
|
|
6
6
|
constructor(projectRoot) {
|
|
7
7
|
this.projectRoot = projectRoot;
|
|
8
|
-
this.processes = new Map(); // subscriber_id ->
|
|
8
|
+
this.processes = new Map(); // subscriber_id -> { child, onExit, onError }
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
/**
|
|
@@ -14,9 +14,7 @@ class AgentProcessManager {
|
|
|
14
14
|
register(subscriberId, childProcess) {
|
|
15
15
|
if (!subscriberId || !childProcess) return;
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
childProcess.on("exit", (code, signal) => {
|
|
17
|
+
const onExit = (code, signal) => {
|
|
20
18
|
this.processes.delete(subscriberId);
|
|
21
19
|
|
|
22
20
|
// 自动清理 bus 状态
|
|
@@ -32,19 +30,26 @@ class AgentProcessManager {
|
|
|
32
30
|
} catch (err) {
|
|
33
31
|
console.error(`[daemon] Failed to cleanup ${subscriberId}:`, err.message);
|
|
34
32
|
}
|
|
35
|
-
}
|
|
33
|
+
};
|
|
36
34
|
|
|
37
|
-
|
|
35
|
+
const onError = (err) => {
|
|
38
36
|
console.error(`[daemon] Agent ${subscriberId} error:`, err.message);
|
|
39
37
|
this.processes.delete(subscriberId);
|
|
38
|
+
};
|
|
39
|
+
this.processes.set(subscriberId, {
|
|
40
|
+
child: childProcess,
|
|
41
|
+
onExit,
|
|
42
|
+
onError,
|
|
40
43
|
});
|
|
44
|
+
childProcess.on("exit", onExit);
|
|
45
|
+
childProcess.on("error", onError);
|
|
41
46
|
}
|
|
42
47
|
|
|
43
48
|
/**
|
|
44
49
|
* 获取运行中的进程
|
|
45
50
|
*/
|
|
46
51
|
get(subscriberId) {
|
|
47
|
-
return this.processes.get(subscriberId);
|
|
52
|
+
return this.processes.get(subscriberId)?.child;
|
|
48
53
|
}
|
|
49
54
|
|
|
50
55
|
/**
|
|
@@ -57,13 +62,28 @@ class AgentProcessManager {
|
|
|
57
62
|
/**
|
|
58
63
|
* 清理所有子进程
|
|
59
64
|
*/
|
|
60
|
-
cleanup() {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
cleanup(options = {}) {
|
|
66
|
+
const terminate = options.terminate !== false;
|
|
67
|
+
for (const [subscriberId, entry] of this.processes.entries()) {
|
|
68
|
+
const { child, onExit, onError } = entry;
|
|
69
|
+
if (terminate) {
|
|
70
|
+
try {
|
|
71
|
+
child.kill("SIGTERM");
|
|
72
|
+
console.log(`[daemon] Killed agent ${subscriberId}`);
|
|
73
|
+
} catch {
|
|
74
|
+
// ignore
|
|
75
|
+
}
|
|
76
|
+
} else {
|
|
77
|
+
// Global-daemon ownership is logical, not a parent/child lifetime
|
|
78
|
+
// contract. Let the runner survive daemon replacement and reconnect
|
|
79
|
+
// through its durable bus state.
|
|
80
|
+
try {
|
|
81
|
+
child.removeListener("exit", onExit);
|
|
82
|
+
child.removeListener("error", onError);
|
|
83
|
+
if (typeof child.unref === "function") child.unref();
|
|
84
|
+
} catch {
|
|
85
|
+
// best-effort detachment
|
|
86
|
+
}
|
|
67
87
|
}
|
|
68
88
|
}
|
|
69
89
|
this.processes.clear();
|
|
@@ -11,7 +11,11 @@ const {
|
|
|
11
11
|
const { subscriberToSafeName } = require("../../coordination/bus/utils");
|
|
12
12
|
const { normalizeReportInput } = require("../../coordination/report/store");
|
|
13
13
|
const { enqueueAgentReport } = require("./reportControlBus");
|
|
14
|
-
const { isRunning
|
|
14
|
+
const { isRunning } = require("./index");
|
|
15
|
+
const {
|
|
16
|
+
resolveDaemonEndpoint,
|
|
17
|
+
routeDaemonRequest,
|
|
18
|
+
} = require("./endpoint");
|
|
15
19
|
const { IPC_REQUEST_TYPES } = require("../contracts/eventContract");
|
|
16
20
|
const {
|
|
17
21
|
applyProjectNicknamePrefix,
|
|
@@ -128,11 +132,16 @@ function assertAgentHandle(bus, subscriber, args = {}, options = {}) {
|
|
|
128
132
|
}
|
|
129
133
|
|
|
130
134
|
function notifyDaemonRefresh(projectRoot) {
|
|
131
|
-
|
|
132
|
-
const
|
|
135
|
+
const endpoint = resolveDaemonEndpoint(projectRoot);
|
|
136
|
+
const daemonRoot = endpoint.scope === "global"
|
|
137
|
+
? endpoint.controllerRoot
|
|
138
|
+
: endpoint.projectRoot;
|
|
139
|
+
if (!isRunning(daemonRoot)) return;
|
|
133
140
|
try {
|
|
134
|
-
const client = net.createConnection(
|
|
135
|
-
client.write(`${JSON.stringify({
|
|
141
|
+
const client = net.createConnection(endpoint.socketPath, () => {
|
|
142
|
+
client.write(`${JSON.stringify(routeDaemonRequest(endpoint, {
|
|
143
|
+
type: IPC_REQUEST_TYPES.REFRESH_STATUS,
|
|
144
|
+
}))}\n`);
|
|
136
145
|
client.end();
|
|
137
146
|
});
|
|
138
147
|
client.on("error", () => {});
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
|
|
5
|
+
const {
|
|
6
|
+
loadConfig,
|
|
7
|
+
normalizeDaemonTopology,
|
|
8
|
+
} = require("../../config");
|
|
9
|
+
const { getUfooPaths } = require("../../coordination/state/paths");
|
|
10
|
+
const {
|
|
11
|
+
normalizeProjectRoot,
|
|
12
|
+
resolveGlobalControllerProjectRoot,
|
|
13
|
+
} = require("../projects");
|
|
14
|
+
|
|
15
|
+
function isSocketFile(filePath) {
|
|
16
|
+
try {
|
|
17
|
+
return fs.statSync(filePath).isSocket();
|
|
18
|
+
} catch {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function resolveEffectiveDaemonTopology(projectRoot, options = {}) {
|
|
24
|
+
const config = options.config || loadConfig(projectRoot);
|
|
25
|
+
return normalizeDaemonTopology(
|
|
26
|
+
options.topology
|
|
27
|
+
|| process.env.UFOO_DAEMON_TOPOLOGY
|
|
28
|
+
|| config.daemonTopology
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function resolveDaemonEndpoint(projectRoot, options = {}) {
|
|
33
|
+
const canonicalRoot = normalizeProjectRoot(projectRoot);
|
|
34
|
+
const controllerRoot = normalizeProjectRoot(
|
|
35
|
+
options.controllerRoot || resolveGlobalControllerProjectRoot()
|
|
36
|
+
);
|
|
37
|
+
const topology = resolveEffectiveDaemonTopology(canonicalRoot, options);
|
|
38
|
+
const projectSocket = getUfooPaths(canonicalRoot).ufooSock;
|
|
39
|
+
const globalSocket = getUfooPaths(controllerRoot).ufooSock;
|
|
40
|
+
let scope = "project";
|
|
41
|
+
let sockPath = projectSocket;
|
|
42
|
+
|
|
43
|
+
if (canonicalRoot === controllerRoot || topology === "global") {
|
|
44
|
+
scope = "global";
|
|
45
|
+
sockPath = globalSocket;
|
|
46
|
+
} else if (topology === "hybrid") {
|
|
47
|
+
const preferCompatibilitySocket = options.preferCompatibilitySocket !== false;
|
|
48
|
+
if (!preferCompatibilitySocket || !isSocketFile(projectSocket)) {
|
|
49
|
+
scope = "global";
|
|
50
|
+
sockPath = globalSocket;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
topology,
|
|
56
|
+
scope,
|
|
57
|
+
socketPath: sockPath,
|
|
58
|
+
projectRoot: canonicalRoot,
|
|
59
|
+
controllerRoot,
|
|
60
|
+
routeProjectRoot: scope === "global" && canonicalRoot !== controllerRoot
|
|
61
|
+
? canonicalRoot
|
|
62
|
+
: "",
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function routeDaemonRequest(endpoint, request = {}) {
|
|
67
|
+
if (!endpoint || !endpoint.routeProjectRoot) return { ...request };
|
|
68
|
+
return {
|
|
69
|
+
...request,
|
|
70
|
+
project_root: request.project_root || request.projectRoot || endpoint.routeProjectRoot,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
module.exports = {
|
|
75
|
+
isSocketFile,
|
|
76
|
+
resolveEffectiveDaemonTopology,
|
|
77
|
+
resolveDaemonEndpoint,
|
|
78
|
+
routeDaemonRequest,
|
|
79
|
+
};
|