@love-moon/conductor-cli 0.2.32 → 0.2.33
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/bin/conductor-fire.js +201 -68
- package/package.json +4 -4
- package/src/daemon.js +772 -52
- package/src/fire/resume.js +67 -7
- package/src/runtime-backends.js +306 -3
package/src/fire/resume.js
CHANGED
|
@@ -7,9 +7,11 @@ import crypto from "node:crypto";
|
|
|
7
7
|
|
|
8
8
|
import yaml from "js-yaml";
|
|
9
9
|
import {
|
|
10
|
+
filterRuntimeSupportedAllowCliList,
|
|
10
11
|
getExternalRuntimeBackendDescriptor,
|
|
11
12
|
isRuntimeSupportedBackend,
|
|
12
13
|
normalizeRuntimeBackendAlias,
|
|
14
|
+
resolveConfiguredRuntimeBackend,
|
|
13
15
|
} from "../runtime-backends.js";
|
|
14
16
|
|
|
15
17
|
function normalizeBackend(backend) {
|
|
@@ -27,6 +29,18 @@ function normalizeSessionId(sessionId) {
|
|
|
27
29
|
return typeof sessionId === "string" ? sessionId.trim() : "";
|
|
28
30
|
}
|
|
29
31
|
|
|
32
|
+
function resolveConfigFilePath(options = {}) {
|
|
33
|
+
const configuredPath =
|
|
34
|
+
typeof options?.configFilePath === "string" && options.configFilePath.trim()
|
|
35
|
+
? options.configFilePath.trim()
|
|
36
|
+
: typeof process.env.CONDUCTOR_CONFIG === "string" && process.env.CONDUCTOR_CONFIG.trim()
|
|
37
|
+
? process.env.CONDUCTOR_CONFIG.trim()
|
|
38
|
+
: "";
|
|
39
|
+
return configuredPath
|
|
40
|
+
? path.resolve(configuredPath)
|
|
41
|
+
: path.join(resolveHomeDir(options), ".conductor", "config.yaml");
|
|
42
|
+
}
|
|
43
|
+
|
|
30
44
|
export function buildResumeArgsForBackend(backend, sessionId) {
|
|
31
45
|
const resumeSessionId = normalizeSessionId(sessionId);
|
|
32
46
|
if (!resumeSessionId) {
|
|
@@ -442,6 +456,40 @@ async function loadConductorSessionRecords(options = {}) {
|
|
|
442
456
|
return records;
|
|
443
457
|
}
|
|
444
458
|
|
|
459
|
+
async function loadConfiguredAllowCliList(options = {}) {
|
|
460
|
+
const configFilePath = resolveConfigFilePath(options);
|
|
461
|
+
let parsed = null;
|
|
462
|
+
try {
|
|
463
|
+
const content = await fsp.readFile(configFilePath, "utf8");
|
|
464
|
+
parsed = yaml.load(content);
|
|
465
|
+
} catch {
|
|
466
|
+
return {};
|
|
467
|
+
}
|
|
468
|
+
if (!parsed || typeof parsed !== "object" || !parsed.allow_cli_list || typeof parsed.allow_cli_list !== "object") {
|
|
469
|
+
return {};
|
|
470
|
+
}
|
|
471
|
+
return filterRuntimeSupportedAllowCliList(parsed.allow_cli_list, { configFilePath });
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
async function resolveResumeLookupBackend(backend, options = {}) {
|
|
475
|
+
const normalizedBackend = normalizeBackend(backend);
|
|
476
|
+
if (!normalizedBackend) {
|
|
477
|
+
return "";
|
|
478
|
+
}
|
|
479
|
+
const configFilePath = resolveConfigFilePath(options);
|
|
480
|
+
const allowCliList =
|
|
481
|
+
options.allowCliList && typeof options.allowCliList === "object"
|
|
482
|
+
? options.allowCliList
|
|
483
|
+
: await loadConfiguredAllowCliList({ ...options, configFilePath });
|
|
484
|
+
const configuredBackend = await resolveConfiguredRuntimeBackend(normalizedBackend, allowCliList, {
|
|
485
|
+
configFilePath,
|
|
486
|
+
});
|
|
487
|
+
if (configuredBackend?.runtimeBackend) {
|
|
488
|
+
return configuredBackend.runtimeBackend;
|
|
489
|
+
}
|
|
490
|
+
return normalizeRuntimeBackendAlias(normalizedBackend, { configFilePath });
|
|
491
|
+
}
|
|
492
|
+
|
|
445
493
|
function normalizeProjectPathCandidate(value) {
|
|
446
494
|
return typeof value === "string" && value.trim() ? value.trim() : "";
|
|
447
495
|
}
|
|
@@ -451,11 +499,13 @@ function normalizeConductorRecordSourcePath(value) {
|
|
|
451
499
|
}
|
|
452
500
|
|
|
453
501
|
async function resolveExternalResumeContext(backend, sessionId, options = {}) {
|
|
454
|
-
const configFilePath =
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
502
|
+
const configFilePath = resolveConfigFilePath(options);
|
|
503
|
+
const allowCliList = await loadConfiguredAllowCliList({ ...options, configFilePath });
|
|
504
|
+
const normalizedBackend = await resolveResumeLookupBackend(backend, {
|
|
505
|
+
...options,
|
|
506
|
+
configFilePath,
|
|
507
|
+
allowCliList,
|
|
508
|
+
});
|
|
459
509
|
if (!normalizedBackend || resumeProviderForBackend(normalizedBackend)) {
|
|
460
510
|
return null;
|
|
461
511
|
}
|
|
@@ -495,7 +545,11 @@ async function resolveExternalResumeContext(backend, sessionId, options = {}) {
|
|
|
495
545
|
const records = await loadConductorSessionRecords(options);
|
|
496
546
|
for (const record of records) {
|
|
497
547
|
const recordSessionId = normalizeSessionId(record?.session_id);
|
|
498
|
-
const recordBackend =
|
|
548
|
+
const recordBackend = await resolveResumeLookupBackend(record?.backend_type, {
|
|
549
|
+
...options,
|
|
550
|
+
configFilePath,
|
|
551
|
+
allowCliList,
|
|
552
|
+
});
|
|
499
553
|
const projectPath = normalizeProjectPathCandidate(record?.project_path);
|
|
500
554
|
if (recordSessionId !== sessionId || recordBackend !== normalizedBackend || !projectPath) {
|
|
501
555
|
continue;
|
|
@@ -552,6 +606,8 @@ async function resolveKimiResumeCwd(sessionPath, sessionId, options = {}) {
|
|
|
552
606
|
}
|
|
553
607
|
|
|
554
608
|
const records = await loadConductorSessionRecords(options);
|
|
609
|
+
const configFilePath = resolveConfigFilePath(options);
|
|
610
|
+
const allowCliList = await loadConfiguredAllowCliList({ ...options, configFilePath });
|
|
555
611
|
const bySessionId = [];
|
|
556
612
|
const byHash = [];
|
|
557
613
|
|
|
@@ -560,7 +616,11 @@ async function resolveKimiResumeCwd(sessionPath, sessionId, options = {}) {
|
|
|
560
616
|
if (!projectPath) {
|
|
561
617
|
continue;
|
|
562
618
|
}
|
|
563
|
-
const backendType =
|
|
619
|
+
const backendType = await resolveResumeLookupBackend(record?.backend_type, {
|
|
620
|
+
...options,
|
|
621
|
+
configFilePath,
|
|
622
|
+
allowCliList,
|
|
623
|
+
});
|
|
564
624
|
const recordSessionId = normalizeSessionId(record?.session_id);
|
|
565
625
|
const projectHash = md5Hex(projectPath);
|
|
566
626
|
if (
|
package/src/runtime-backends.js
CHANGED
|
@@ -33,6 +33,181 @@ function normalizeRuntimeBackendName(backend) {
|
|
|
33
33
|
return String(backend || "").trim().toLowerCase();
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
function stripExecutableSuffix(name) {
|
|
37
|
+
return String(name || "")
|
|
38
|
+
.trim()
|
|
39
|
+
.toLowerCase()
|
|
40
|
+
.replace(/\.(cmd|bat|exe)$/i, "");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function parseCommandParts(commandLine) {
|
|
44
|
+
const input = String(commandLine || "").trim();
|
|
45
|
+
if (!input) {
|
|
46
|
+
return { command: "", args: [], parts: [] };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const parts = [];
|
|
50
|
+
let current = "";
|
|
51
|
+
let quote = "";
|
|
52
|
+
let escaping = false;
|
|
53
|
+
let tokenStarted = false;
|
|
54
|
+
|
|
55
|
+
for (const char of input) {
|
|
56
|
+
if (escaping) {
|
|
57
|
+
current += char;
|
|
58
|
+
tokenStarted = true;
|
|
59
|
+
escaping = false;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (char === "\\") {
|
|
64
|
+
escaping = true;
|
|
65
|
+
tokenStarted = true;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (quote) {
|
|
70
|
+
if (char === quote) {
|
|
71
|
+
quote = "";
|
|
72
|
+
} else {
|
|
73
|
+
current += char;
|
|
74
|
+
}
|
|
75
|
+
tokenStarted = true;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (char === "'" || char === "\"") {
|
|
80
|
+
quote = char;
|
|
81
|
+
tokenStarted = true;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (/\s/.test(char)) {
|
|
86
|
+
if (tokenStarted) {
|
|
87
|
+
parts.push(current);
|
|
88
|
+
current = "";
|
|
89
|
+
tokenStarted = false;
|
|
90
|
+
}
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
current += char;
|
|
95
|
+
tokenStarted = true;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (tokenStarted) {
|
|
99
|
+
parts.push(current);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
command: parts[0] || "",
|
|
104
|
+
args: parts.slice(1),
|
|
105
|
+
parts,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function isEnvironmentAssignment(token) {
|
|
110
|
+
return /^[A-Za-z_][A-Za-z0-9_]*=/.test(String(token || "").trim());
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function collectExecutableCandidates(commandLine) {
|
|
114
|
+
const { parts } = parseCommandParts(commandLine);
|
|
115
|
+
if (!parts.length) {
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const candidates = [];
|
|
120
|
+
const pushCandidate = (token) => {
|
|
121
|
+
const executable = stripExecutableSuffix(path.basename(String(token || "").trim()));
|
|
122
|
+
if (!executable || candidates.includes(executable)) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
candidates.push(executable);
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const findFirstCommandTokenIndex = (startIndex, { skipEnvAssignments = false } = {}) => {
|
|
129
|
+
for (let index = startIndex; index < parts.length; index += 1) {
|
|
130
|
+
const token = String(parts[index] || "").trim();
|
|
131
|
+
if (!token || token === "--") {
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (skipEnvAssignments && isEnvironmentAssignment(token)) {
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
if (token.startsWith("-")) {
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
return index;
|
|
141
|
+
}
|
|
142
|
+
return -1;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const walkCommand = (startIndex, { skipEnvAssignments = false } = {}) => {
|
|
146
|
+
const tokenIndex = findFirstCommandTokenIndex(startIndex, { skipEnvAssignments });
|
|
147
|
+
if (tokenIndex === -1) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const token = parts[tokenIndex];
|
|
152
|
+
pushCandidate(token);
|
|
153
|
+
|
|
154
|
+
const executable = stripExecutableSuffix(path.basename(token));
|
|
155
|
+
if (executable === "env") {
|
|
156
|
+
walkCommand(tokenIndex + 1, { skipEnvAssignments: true });
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (executable === "npx" || executable === "pnpx" || executable === "bunx") {
|
|
161
|
+
walkCommand(tokenIndex + 1);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (executable === "pnpm" || executable === "yarn" || executable === "npm") {
|
|
166
|
+
const subcommandIndex = findFirstCommandTokenIndex(tokenIndex + 1);
|
|
167
|
+
if (subcommandIndex === -1) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
const normalizedSubcommand = stripExecutableSuffix(path.basename(parts[subcommandIndex]));
|
|
171
|
+
if (normalizedSubcommand === "exec" || normalizedSubcommand === "dlx") {
|
|
172
|
+
walkCommand(subcommandIndex + 1);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
walkCommand(0);
|
|
178
|
+
|
|
179
|
+
return candidates;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export function inferBuiltInRuntimeBackendFromCommand(commandLine) {
|
|
183
|
+
const candidates = collectExecutableCandidates(commandLine);
|
|
184
|
+
for (const executable of candidates) {
|
|
185
|
+
if (BUILT_IN_RUNTIME_BACKEND_SET.has(executable)) {
|
|
186
|
+
return executable;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return "";
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async function inferRuntimeBackendFromCommand(commandLine, options = {}) {
|
|
193
|
+
const builtInBackend = inferBuiltInRuntimeBackendFromCommand(commandLine);
|
|
194
|
+
if (builtInBackend) {
|
|
195
|
+
return builtInBackend;
|
|
196
|
+
}
|
|
197
|
+
const candidates = collectExecutableCandidates(commandLine);
|
|
198
|
+
for (const executable of candidates) {
|
|
199
|
+
const resolvedBackend = await normalizeRuntimeBackendAlias(executable, options);
|
|
200
|
+
if (await isRuntimeSupportedBackend(resolvedBackend, options)) {
|
|
201
|
+
return resolvedBackend;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return "";
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export function isBuiltInRuntimeBackend(backend) {
|
|
208
|
+
return BUILT_IN_RUNTIME_BACKEND_SET.has(normalizeRuntimeBackendName(backend));
|
|
209
|
+
}
|
|
210
|
+
|
|
36
211
|
function readConfigEnvValue(configFilePath, key) {
|
|
37
212
|
const targetPath =
|
|
38
213
|
typeof configFilePath === "string" && configFilePath.trim()
|
|
@@ -227,8 +402,8 @@ export async function filterRuntimeSupportedAllowCliList(allowCliList, options =
|
|
|
227
402
|
}
|
|
228
403
|
const filtered = {};
|
|
229
404
|
for (const [backend, command] of Object.entries(allowCliList)) {
|
|
230
|
-
const normalizedBackend =
|
|
231
|
-
if (!
|
|
405
|
+
const normalizedBackend = normalizeRuntimeBackendName(backend);
|
|
406
|
+
if (!normalizedBackend || LEGACY_RUNTIME_BACKEND_ALIASES.has(normalizedBackend)) {
|
|
232
407
|
continue;
|
|
233
408
|
}
|
|
234
409
|
if (typeof command !== "string" || !command.trim()) {
|
|
@@ -237,11 +412,139 @@ export async function filterRuntimeSupportedAllowCliList(allowCliList, options =
|
|
|
237
412
|
if (filtered[normalizedBackend] !== undefined) {
|
|
238
413
|
continue;
|
|
239
414
|
}
|
|
240
|
-
|
|
415
|
+
try {
|
|
416
|
+
const inferredRuntimeBackend = await inferRuntimeBackendFromCommand(command, options);
|
|
417
|
+
if (inferredRuntimeBackend) {
|
|
418
|
+
filtered[normalizedBackend] = command.trim();
|
|
419
|
+
continue;
|
|
420
|
+
}
|
|
421
|
+
} catch {
|
|
422
|
+
// Ignore external provider discovery failures here so built-in aliases inferred from
|
|
423
|
+
// the command line can still pass through independently.
|
|
424
|
+
}
|
|
425
|
+
try {
|
|
426
|
+
const resolvedBackend = await normalizeRuntimeBackendAlias(normalizedBackend, options);
|
|
427
|
+
const isSupportedResolvedBackend = await isRuntimeSupportedBackend(resolvedBackend, options);
|
|
428
|
+
if (!isSupportedResolvedBackend) {
|
|
429
|
+
continue;
|
|
430
|
+
}
|
|
431
|
+
filtered[normalizedBackend] = command.trim();
|
|
432
|
+
} catch {
|
|
433
|
+
// Skip broken external entries here; callers that actually need external backends
|
|
434
|
+
// can surface discovery failures from the advertised-backend path.
|
|
435
|
+
}
|
|
241
436
|
}
|
|
242
437
|
return filtered;
|
|
243
438
|
}
|
|
244
439
|
|
|
440
|
+
export async function resolveConfiguredRuntimeBackend(backend, allowCliList, options = {}) {
|
|
441
|
+
const normalizedBackend = normalizeRuntimeBackendName(backend);
|
|
442
|
+
if (!normalizedBackend || LEGACY_RUNTIME_BACKEND_ALIASES.has(normalizedBackend)) {
|
|
443
|
+
return null;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
const configuredCommand =
|
|
447
|
+
allowCliList && typeof allowCliList === "object" && typeof allowCliList[normalizedBackend] === "string"
|
|
448
|
+
? allowCliList[normalizedBackend].trim()
|
|
449
|
+
: "";
|
|
450
|
+
const hasConfiguredEntry = Boolean(configuredCommand);
|
|
451
|
+
if (hasConfiguredEntry) {
|
|
452
|
+
const inferredRuntimeBackend = await inferRuntimeBackendFromCommand(configuredCommand, options);
|
|
453
|
+
if (inferredRuntimeBackend) {
|
|
454
|
+
return {
|
|
455
|
+
requestedBackend: normalizedBackend,
|
|
456
|
+
runtimeBackend: inferredRuntimeBackend,
|
|
457
|
+
commandLine: configuredCommand,
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
const resolvedBackend = await normalizeRuntimeBackendAlias(normalizedBackend, options);
|
|
462
|
+
if (hasConfiguredEntry && await isRuntimeSupportedBackend(resolvedBackend, options)) {
|
|
463
|
+
return {
|
|
464
|
+
requestedBackend: normalizedBackend,
|
|
465
|
+
runtimeBackend: resolvedBackend,
|
|
466
|
+
commandLine: configuredCommand,
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
if (!hasConfiguredEntry && !isBuiltInRuntimeBackend(resolvedBackend) && await isRuntimeSupportedBackend(resolvedBackend, options)) {
|
|
471
|
+
return {
|
|
472
|
+
requestedBackend: normalizedBackend,
|
|
473
|
+
runtimeBackend: resolvedBackend,
|
|
474
|
+
commandLine: "",
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
return null;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
export async function listAdvertisedBackends(allowCliList, options = {}) {
|
|
481
|
+
const filteredAllowCliList =
|
|
482
|
+
allowCliList && typeof allowCliList === "object"
|
|
483
|
+
? Object.fromEntries(
|
|
484
|
+
Object.entries(allowCliList)
|
|
485
|
+
.map(([backend, command]) => [normalizeRuntimeBackendName(backend), typeof command === "string" ? command.trim() : ""])
|
|
486
|
+
.filter(([backend, command]) => backend && command),
|
|
487
|
+
)
|
|
488
|
+
: {};
|
|
489
|
+
const configuredBackends = Object.keys(filteredAllowCliList);
|
|
490
|
+
let runtimeBackends = [...BUILT_IN_RUNTIME_BACKENDS];
|
|
491
|
+
let discoveryError = null;
|
|
492
|
+
try {
|
|
493
|
+
runtimeBackends = await listRuntimeSupportedBackends(options);
|
|
494
|
+
} catch (error) {
|
|
495
|
+
discoveryError = error;
|
|
496
|
+
}
|
|
497
|
+
const discoveredExternalBackends = runtimeBackends.filter((backend) => !BUILT_IN_RUNTIME_BACKEND_SET.has(backend));
|
|
498
|
+
const advertisedConfiguredBackends = [];
|
|
499
|
+
const runtimeBackendMap = {};
|
|
500
|
+
|
|
501
|
+
for (const backend of configuredBackends) {
|
|
502
|
+
try {
|
|
503
|
+
const configuredBackend = await resolveConfiguredRuntimeBackend(backend, filteredAllowCliList, options);
|
|
504
|
+
if (!configuredBackend?.runtimeBackend) {
|
|
505
|
+
continue;
|
|
506
|
+
}
|
|
507
|
+
advertisedConfiguredBackends.push(backend);
|
|
508
|
+
runtimeBackendMap[backend] = configuredBackend.runtimeBackend;
|
|
509
|
+
} catch (error) {
|
|
510
|
+
discoveryError ||= error;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
const explicitlyConfiguredBackends = new Set(advertisedConfiguredBackends);
|
|
515
|
+
const shadowedExternalBackends = new Set();
|
|
516
|
+
|
|
517
|
+
for (const backend of advertisedConfiguredBackends) {
|
|
518
|
+
const runtimeBackend = runtimeBackendMap[backend] || "";
|
|
519
|
+
if (
|
|
520
|
+
!runtimeBackend ||
|
|
521
|
+
isBuiltInRuntimeBackend(runtimeBackend) ||
|
|
522
|
+
runtimeBackend === backend ||
|
|
523
|
+
explicitlyConfiguredBackends.has(runtimeBackend)
|
|
524
|
+
) {
|
|
525
|
+
continue;
|
|
526
|
+
}
|
|
527
|
+
shadowedExternalBackends.add(runtimeBackend);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
const externalBackends = discoveredExternalBackends.filter(
|
|
531
|
+
(backend) => !shadowedExternalBackends.has(backend) && !explicitlyConfiguredBackends.has(backend),
|
|
532
|
+
);
|
|
533
|
+
const supportedBackends = [...new Set([...advertisedConfiguredBackends, ...externalBackends])];
|
|
534
|
+
|
|
535
|
+
for (const backend of externalBackends) {
|
|
536
|
+
runtimeBackendMap[backend] = backend;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
return {
|
|
540
|
+
configuredBackends: advertisedConfiguredBackends,
|
|
541
|
+
externalBackends,
|
|
542
|
+
supportedBackends,
|
|
543
|
+
runtimeBackendMap,
|
|
544
|
+
discoveryError,
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
|
|
245
548
|
export { BUILT_IN_RUNTIME_BACKENDS as RUNTIME_SUPPORTED_BACKENDS, normalizeRuntimeBackendName };
|
|
246
549
|
|
|
247
550
|
export function resetRuntimeBackendCacheForTests() {
|