@mcoda/mswarm 0.1.107 → 0.1.109
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 +17 -0
- package/dist/codali-executor.d.ts.map +1 -1
- package/dist/codali-executor.js +17 -3
- package/dist/codali-executor.js.map +1 -1
- package/dist/runtime.d.ts +16 -5
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +292 -36
- package/dist/runtime.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +4 -0
- package/dist/server.js.map +1 -1
- package/dist/vendor/codali/agents/AgentInventory.d.ts +56 -0
- package/dist/vendor/codali/agents/AgentInventory.d.ts.map +1 -0
- package/dist/vendor/codali/agents/AgentInventory.js +131 -0
- package/dist/vendor/codali/agents/RoleResolution.d.ts +49 -0
- package/dist/vendor/codali/agents/RoleResolution.d.ts.map +1 -0
- package/dist/vendor/codali/agents/RoleResolution.js +166 -0
- package/dist/vendor/codali/api/ChatCompletionsAdapter.d.ts +76 -0
- package/dist/vendor/codali/api/ChatCompletionsAdapter.d.ts.map +1 -0
- package/dist/vendor/codali/api/ChatCompletionsAdapter.js +54 -0
- package/dist/vendor/codali/api/CodaliApi.d.ts +78 -0
- package/dist/vendor/codali/api/CodaliApi.d.ts.map +1 -0
- package/dist/vendor/codali/api/CodaliApi.js +273 -0
- package/dist/vendor/codali/api/CodaliServer.d.ts +42 -0
- package/dist/vendor/codali/api/CodaliServer.d.ts.map +1 -0
- package/dist/vendor/codali/api/CodaliServer.js +118 -0
- package/dist/vendor/codali/cli/AskCommand.d.ts +66 -0
- package/dist/vendor/codali/cli/AskCommand.d.ts.map +1 -0
- package/dist/vendor/codali/cli/AskCommand.js +536 -0
- package/dist/vendor/codali/cli/AuthCommand.d.ts +19 -0
- package/dist/vendor/codali/cli/AuthCommand.d.ts.map +1 -0
- package/dist/vendor/codali/cli/AuthCommand.js +161 -0
- package/dist/vendor/codali/cli/ServeCommand.d.ts +5 -0
- package/dist/vendor/codali/cli/ServeCommand.d.ts.map +1 -0
- package/dist/vendor/codali/cli/ServeCommand.js +87 -0
- package/dist/vendor/codali/cli/ToolsCommand.d.ts +22 -0
- package/dist/vendor/codali/cli/ToolsCommand.d.ts.map +1 -0
- package/dist/vendor/codali/cli/ToolsCommand.js +208 -0
- package/dist/vendor/codali/cli.d.ts.map +1 -1
- package/dist/vendor/codali/cli.js +29 -1
- package/dist/vendor/codali/connectors/http/HttpToolDefinition.d.ts +74 -0
- package/dist/vendor/codali/connectors/http/HttpToolDefinition.d.ts.map +1 -0
- package/dist/vendor/codali/connectors/http/HttpToolDefinition.js +190 -0
- package/dist/vendor/codali/connectors/http/HttpToolSource.d.ts +18 -0
- package/dist/vendor/codali/connectors/http/HttpToolSource.d.ts.map +1 -0
- package/dist/vendor/codali/connectors/http/HttpToolSource.js +29 -0
- package/dist/vendor/codali/connectors/mcp/McpClient.d.ts +132 -0
- package/dist/vendor/codali/connectors/mcp/McpClient.d.ts.map +1 -0
- package/dist/vendor/codali/connectors/mcp/McpClient.js +215 -0
- package/dist/vendor/codali/connectors/mcp/McpServerRegistry.d.ts +75 -0
- package/dist/vendor/codali/connectors/mcp/McpServerRegistry.d.ts.map +1 -0
- package/dist/vendor/codali/connectors/mcp/McpServerRegistry.js +180 -0
- package/dist/vendor/codali/connectors/mcp/McpToolAdapter.d.ts +48 -0
- package/dist/vendor/codali/connectors/mcp/McpToolAdapter.d.ts.map +1 -0
- package/dist/vendor/codali/connectors/mcp/McpToolAdapter.js +101 -0
- package/dist/vendor/codali/connectors/mcp/McpToolSource.d.ts +27 -0
- package/dist/vendor/codali/connectors/mcp/McpToolSource.d.ts.map +1 -0
- package/dist/vendor/codali/connectors/mcp/McpToolSource.js +33 -0
- package/dist/vendor/codali/connectors/media/ImageGenerationTool.d.ts +43 -0
- package/dist/vendor/codali/connectors/media/ImageGenerationTool.d.ts.map +1 -0
- package/dist/vendor/codali/connectors/media/ImageGenerationTool.js +122 -0
- package/dist/vendor/codali/connectors/oauth/DeviceCodeAuth.d.ts +74 -0
- package/dist/vendor/codali/connectors/oauth/DeviceCodeAuth.d.ts.map +1 -0
- package/dist/vendor/codali/connectors/oauth/DeviceCodeAuth.js +153 -0
- package/dist/vendor/codali/docdex/DocdexClient.d.ts +2 -0
- package/dist/vendor/codali/docdex/DocdexClient.d.ts.map +1 -1
- package/dist/vendor/codali/docdex/DocdexClient.js +4 -0
- package/dist/vendor/codali/gateway/CodaliGateway.d.ts +51 -3
- package/dist/vendor/codali/gateway/CodaliGateway.d.ts.map +1 -1
- package/dist/vendor/codali/gateway/CodaliGateway.js +226 -2
- package/dist/vendor/codali/gateway/CodaliGatewayTypes.d.ts +34 -0
- package/dist/vendor/codali/gateway/CodaliGatewayTypes.d.ts.map +1 -1
- package/dist/vendor/codali/gateway/EvidenceNormalizer.d.ts.map +1 -1
- package/dist/vendor/codali/gateway/EvidenceNormalizer.js +90 -1
- package/dist/vendor/codali/gateway/Finalizer.d.ts +50 -0
- package/dist/vendor/codali/gateway/Finalizer.d.ts.map +1 -0
- package/dist/vendor/codali/gateway/Finalizer.js +79 -0
- package/dist/vendor/codali/gateway/GatewayPlanner.d.ts +13 -0
- package/dist/vendor/codali/gateway/GatewayPlanner.d.ts.map +1 -1
- package/dist/vendor/codali/gateway/GatewayPlanner.js +81 -16
- package/dist/vendor/codali/gateway/GatewayStateMachine.d.ts.map +1 -1
- package/dist/vendor/codali/gateway/GatewayStateMachine.js +6 -0
- package/dist/vendor/codali/gateway/GatewayTracer.d.ts +93 -0
- package/dist/vendor/codali/gateway/GatewayTracer.d.ts.map +1 -0
- package/dist/vendor/codali/gateway/GatewayTracer.js +214 -0
- package/dist/vendor/codali/gateway/LocalGatewayProvider.d.ts +46 -0
- package/dist/vendor/codali/gateway/LocalGatewayProvider.d.ts.map +1 -0
- package/dist/vendor/codali/gateway/LocalGatewayProvider.js +161 -0
- package/dist/vendor/codali/gateway/LocalGatewayTaskRunner.d.ts +79 -0
- package/dist/vendor/codali/gateway/LocalGatewayTaskRunner.d.ts.map +1 -0
- package/dist/vendor/codali/gateway/LocalGatewayTaskRunner.js +354 -0
- package/dist/vendor/codali/gateway/RelevanceJudge.d.ts +14 -0
- package/dist/vendor/codali/gateway/RelevanceJudge.d.ts.map +1 -0
- package/dist/vendor/codali/gateway/RelevanceJudge.js +87 -0
- package/dist/vendor/codali/gateway/ResponseSchema.d.ts +32 -0
- package/dist/vendor/codali/gateway/ResponseSchema.d.ts.map +1 -0
- package/dist/vendor/codali/gateway/ResponseSchema.js +120 -0
- package/dist/vendor/codali/gateway/TemporalContext.d.ts +36 -0
- package/dist/vendor/codali/gateway/TemporalContext.d.ts.map +1 -0
- package/dist/vendor/codali/gateway/TemporalContext.js +115 -0
- package/dist/vendor/codali/gateway/ToolCapabilityCompiler.d.ts +5 -0
- package/dist/vendor/codali/gateway/ToolCapabilityCompiler.d.ts.map +1 -1
- package/dist/vendor/codali/gateway/ToolCapabilityCompiler.js +27 -2
- package/dist/vendor/codali/gateway/ToolDescriptorSource.d.ts +6 -0
- package/dist/vendor/codali/gateway/ToolDescriptorSource.d.ts.map +1 -0
- package/dist/vendor/codali/gateway/ToolDescriptorSource.js +30 -0
- package/dist/vendor/codali/gateway/ToolExposure.d.ts +36 -0
- package/dist/vendor/codali/gateway/ToolExposure.d.ts.map +1 -0
- package/dist/vendor/codali/gateway/ToolExposure.js +154 -0
- package/dist/vendor/codali/index.d.ts +57 -0
- package/dist/vendor/codali/index.d.ts.map +1 -1
- package/dist/vendor/codali/index.js +45 -0
- package/dist/vendor/codali/providers/ClaudeCliProvider.d.ts +2 -0
- package/dist/vendor/codali/providers/ClaudeCliProvider.d.ts.map +1 -1
- package/dist/vendor/codali/providers/ClaudeCliProvider.js +2 -0
- package/dist/vendor/codali/providers/CodexCliProvider.d.ts +2 -0
- package/dist/vendor/codali/providers/CodexCliProvider.d.ts.map +1 -1
- package/dist/vendor/codali/providers/CodexCliProvider.js +2 -0
- package/dist/vendor/codali/providers/MswarmWorkerProvider.d.ts +2 -0
- package/dist/vendor/codali/providers/MswarmWorkerProvider.d.ts.map +1 -1
- package/dist/vendor/codali/providers/MswarmWorkerProvider.js +2 -0
- package/dist/vendor/codali/providers/OllamaRemoteProvider.d.ts +2 -0
- package/dist/vendor/codali/providers/OllamaRemoteProvider.d.ts.map +1 -1
- package/dist/vendor/codali/providers/OllamaRemoteProvider.js +2 -0
- package/dist/vendor/codali/providers/OpenAiCompatibleProvider.d.ts +2 -0
- package/dist/vendor/codali/providers/OpenAiCompatibleProvider.d.ts.map +1 -1
- package/dist/vendor/codali/providers/OpenAiCompatibleProvider.js +2 -0
- package/dist/vendor/codali/providers/ProviderTypes.d.ts +13 -0
- package/dist/vendor/codali/providers/ProviderTypes.d.ts.map +1 -1
- package/dist/vendor/codali/providers/ProviderTypes.js +1 -1
- package/dist/vendor/codali/runcontext/CredentialFile.d.ts +12 -0
- package/dist/vendor/codali/runcontext/CredentialFile.d.ts.map +1 -0
- package/dist/vendor/codali/runcontext/CredentialFile.js +81 -0
- package/dist/vendor/codali/runcontext/RunContextResolver.d.ts +126 -0
- package/dist/vendor/codali/runcontext/RunContextResolver.d.ts.map +1 -0
- package/dist/vendor/codali/runcontext/RunContextResolver.js +389 -0
- package/dist/vendor/codali/storage/CodaliDatasetPrivacyEngine.d.ts +1 -1
- package/dist/vendor/codali/storage/CodaliDatasetPrivacyEngine.d.ts.map +1 -1
- package/dist/vendor/codali/storage/CodaliDatasetPrivacyEngine.js +12 -0
- package/dist/vendor/codali/tools/ToolRegistry.d.ts +14 -1
- package/dist/vendor/codali/tools/ToolRegistry.d.ts.map +1 -1
- package/dist/vendor/codali/tools/ToolRegistry.js +38 -1
- package/dist/vendor/codali/tools/ToolTypes.d.ts +35 -0
- package/dist/vendor/codali/tools/ToolTypes.d.ts.map +1 -1
- package/dist/vendor/codali/tools/ToolTypes.js +21 -0
- package/dist/vendor/codali/tools/TruncateResult.d.ts +29 -0
- package/dist/vendor/codali/tools/TruncateResult.d.ts.map +1 -0
- package/dist/vendor/codali/tools/TruncateResult.js +91 -0
- package/dist/vendor/codali/tools/diff/DiffTool.d.ts.map +1 -1
- package/dist/vendor/codali/tools/diff/DiffTool.js +2 -0
- package/dist/vendor/codali/tools/docdex/DocdexTools.d.ts +21 -1
- package/dist/vendor/codali/tools/docdex/DocdexTools.d.ts.map +1 -1
- package/dist/vendor/codali/tools/docdex/DocdexTools.js +108 -16
- package/dist/vendor/codali/tools/filesystem/FileTools.d.ts.map +1 -1
- package/dist/vendor/codali/tools/filesystem/FileTools.js +8 -0
- package/dist/vendor/codali/tools/search/SearchTool.d.ts.map +1 -1
- package/dist/vendor/codali/tools/search/SearchTool.js +3 -1
- package/dist/vendor/codali/tools/shell/ShellTool.d.ts.map +1 -1
- package/dist/vendor/codali/tools/shell/ShellTool.js +2 -0
- package/package.json +3 -3
- package/scripts/stable-diffusion-cpp-auth-guard.py +475 -0
- package/scripts/stable-diffusion-cpp-openai-server.py +44 -0
package/dist/runtime.js
CHANGED
|
@@ -62,6 +62,7 @@ const DEFAULT_HEARTBEAT_INTERVAL_SECONDS = 30;
|
|
|
62
62
|
const DEFAULT_SELF_HOSTED_NODE_VERSION = "0.1.70";
|
|
63
63
|
const DEFAULT_REQUEST_TIMEOUT_MS = 10000;
|
|
64
64
|
const DEFAULT_JOB_TIMEOUT_MS = 3600000;
|
|
65
|
+
const DEFAULT_RESULT_POST_TIMEOUT_MS = 5 * 60000;
|
|
65
66
|
const DEFAULT_SERVICE_COMMAND_TIMEOUT_MS = 60000;
|
|
66
67
|
const DEFAULT_CAPABILITY_PROBE_TIMEOUT_MS = 2000;
|
|
67
68
|
const SELF_HOSTED_RUNTIME_PROTOCOL_VERSION = 1;
|
|
@@ -74,6 +75,12 @@ const DEFAULT_MCODA_LIST_ARGS = ["agent", "list", "--json", "--refresh-health"];
|
|
|
74
75
|
const DEFAULT_COMMAND_MAX_BUFFER = 16 * 1024 * 1024;
|
|
75
76
|
const DEFAULT_LOCAL_ARTIFACT_MAX_BYTES = 512 * 1024 * 1024;
|
|
76
77
|
const DEFAULT_JOB_POLL_WAIT_MS = 2000;
|
|
78
|
+
const RESERVED_JOB_POLL_WAIT_MS = 250;
|
|
79
|
+
const MAX_POLL_EXCLUDED_AGENT_SLUGS = 64;
|
|
80
|
+
const MAX_RELAY_AGENT_SLUG_LENGTH = 255;
|
|
81
|
+
const DEFAULT_RESERVED_PRIORITY_MAX = -1;
|
|
82
|
+
const MIN_RELAY_PRIORITY = -100;
|
|
83
|
+
const MAX_RELAY_PRIORITY = 100;
|
|
77
84
|
const JOB_POLL_RETRY_BASE_MS = 1000;
|
|
78
85
|
const JOB_POLL_RETRY_MAX_MS = 60000;
|
|
79
86
|
/**
|
|
@@ -284,6 +291,33 @@ const LOCAL_RUNNER_RESPONSE_FORMAT_ALIASES = {
|
|
|
284
291
|
function optionalText(value) {
|
|
285
292
|
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
286
293
|
}
|
|
294
|
+
function relayJobAgentAliases(job) {
|
|
295
|
+
const aliases = [job.agent_slug, job.source_agent_slug, job.remote_slug]
|
|
296
|
+
.map(optionalText)
|
|
297
|
+
.map((value) => value
|
|
298
|
+
?.toLowerCase()
|
|
299
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
300
|
+
.replace(/-+/g, "-")
|
|
301
|
+
.replace(/^-|-$/g, "") || null)
|
|
302
|
+
.filter((value) => typeof value === "string" && value.length > 0 && value.length <= MAX_RELAY_AGENT_SLUG_LENGTH);
|
|
303
|
+
return [...new Set(aliases)];
|
|
304
|
+
}
|
|
305
|
+
function boundedRelayAgentAliases(activeAliases) {
|
|
306
|
+
const aliases = [];
|
|
307
|
+
const seen = new Set();
|
|
308
|
+
for (const group of activeAliases) {
|
|
309
|
+
for (const alias of group) {
|
|
310
|
+
if (seen.has(alias))
|
|
311
|
+
continue;
|
|
312
|
+
seen.add(alias);
|
|
313
|
+
aliases.push(alias);
|
|
314
|
+
if (aliases.length >= MAX_POLL_EXCLUDED_AGENT_SLUGS) {
|
|
315
|
+
return aliases;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return aliases;
|
|
320
|
+
}
|
|
287
321
|
function isRecord(value) {
|
|
288
322
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
289
323
|
}
|
|
@@ -588,6 +622,39 @@ function parsePositiveInteger(value, fallback) {
|
|
|
588
622
|
}
|
|
589
623
|
return Math.floor(parsed);
|
|
590
624
|
}
|
|
625
|
+
function parseNonNegativeInteger(value, fallback) {
|
|
626
|
+
const parsed = value === undefined || value === null || value === ""
|
|
627
|
+
? fallback
|
|
628
|
+
: typeof value === "number"
|
|
629
|
+
? value
|
|
630
|
+
: typeof value === "string"
|
|
631
|
+
? Number(value)
|
|
632
|
+
: Number.NaN;
|
|
633
|
+
if (!Number.isInteger(parsed) || parsed < 0) {
|
|
634
|
+
throw new Error("MSWARM_SELF_HOSTED_RESERVED_LLM_JOBS must be a non-negative integer");
|
|
635
|
+
}
|
|
636
|
+
return parsed;
|
|
637
|
+
}
|
|
638
|
+
function parseRelayPriority(value, fallback) {
|
|
639
|
+
const parsed = value === undefined || value === null || value === ""
|
|
640
|
+
? fallback
|
|
641
|
+
: typeof value === "number"
|
|
642
|
+
? value
|
|
643
|
+
: typeof value === "string"
|
|
644
|
+
? Number(value)
|
|
645
|
+
: Number.NaN;
|
|
646
|
+
if (!Number.isInteger(parsed) || parsed < MIN_RELAY_PRIORITY || parsed > MAX_RELAY_PRIORITY) {
|
|
647
|
+
throw new Error(`MSWARM_SELF_HOSTED_RESERVED_PRIORITY_MAX must be an integer from ${MIN_RELAY_PRIORITY} to ${MAX_RELAY_PRIORITY}`);
|
|
648
|
+
}
|
|
649
|
+
return parsed;
|
|
650
|
+
}
|
|
651
|
+
function assertReservedLlmCapacity(input) {
|
|
652
|
+
const totalLlmJobs = input.maxConcurrentLlmJobs + input.reservedLlmJobs;
|
|
653
|
+
if (totalLlmJobs > input.maxConcurrentJobs) {
|
|
654
|
+
throw new Error("MSWARM_SELF_HOSTED_MAX_CONCURRENT_JOBS must be greater than or equal to " +
|
|
655
|
+
"MSWARM_SELF_HOSTED_MAX_CONCURRENT_LLM_JOBS plus MSWARM_SELF_HOSTED_RESERVED_LLM_JOBS");
|
|
656
|
+
}
|
|
657
|
+
}
|
|
591
658
|
function parseBoolean(value, fallback) {
|
|
592
659
|
if (typeof value === "boolean")
|
|
593
660
|
return value;
|
|
@@ -1280,6 +1347,8 @@ function serviceEnvironment(config, env, homeDir) {
|
|
|
1280
1347
|
MSWARM_SELF_HOSTED_JOB_TIMEOUT_MS: String(config.jobTimeoutMs),
|
|
1281
1348
|
MSWARM_SELF_HOSTED_MAX_CONCURRENT_JOBS: String(config.maxConcurrentJobs || 1),
|
|
1282
1349
|
MSWARM_SELF_HOSTED_MAX_CONCURRENT_LLM_JOBS: String(config.maxConcurrentLlmJobs || config.maxConcurrentJobs || 1),
|
|
1350
|
+
MSWARM_SELF_HOSTED_RESERVED_LLM_JOBS: String(config.reservedLlmJobs ?? 0),
|
|
1351
|
+
MSWARM_SELF_HOSTED_RESERVED_PRIORITY_MAX: String(config.reservedPriorityMax ?? DEFAULT_RESERVED_PRIORITY_MAX),
|
|
1283
1352
|
MSWARM_SELF_HOSTED_GENERIC_JOBS_ENABLED: config.genericJobsEnabled ? "true" : "false",
|
|
1284
1353
|
MSWARM_SELF_HOSTED_GENERIC_JOB_TIMEOUT_MS: String(config.genericJobTimeoutMs),
|
|
1285
1354
|
MSWARM_SELF_HOSTED_GENERIC_JOB_MAX_CONCURRENCY: String(config.genericJobMaxConcurrency),
|
|
@@ -1695,6 +1764,9 @@ export async function readSelfHostedNodeConfig(env = process.env) {
|
|
|
1695
1764
|
const packageNodeVersion = await readPackageNodeVersion();
|
|
1696
1765
|
const maxConcurrentJobs = parsePositiveInteger(env.MSWARM_SELF_HOSTED_MAX_CONCURRENT_JOBS, state.max_concurrent_jobs || 1);
|
|
1697
1766
|
const maxConcurrentLlmJobs = parsePositiveInteger(env.MSWARM_SELF_HOSTED_MAX_CONCURRENT_LLM_JOBS, state.max_concurrent_llm_jobs || maxConcurrentJobs);
|
|
1767
|
+
const reservedLlmJobs = parseNonNegativeInteger(env.MSWARM_SELF_HOSTED_RESERVED_LLM_JOBS, state.reserved_llm_jobs ?? 0);
|
|
1768
|
+
const reservedPriorityMax = parseRelayPriority(env.MSWARM_SELF_HOSTED_RESERVED_PRIORITY_MAX, state.reserved_priority_max ?? DEFAULT_RESERVED_PRIORITY_MAX);
|
|
1769
|
+
assertReservedLlmCapacity({ maxConcurrentJobs, maxConcurrentLlmJobs, reservedLlmJobs });
|
|
1698
1770
|
return {
|
|
1699
1771
|
gatewayBaseUrl: trimTrailingSlash(gatewayBaseUrl),
|
|
1700
1772
|
jobsPollPath: optionalText(env.MSWARM_SELF_HOSTED_JOBS_POLL_PATH) ||
|
|
@@ -1738,6 +1810,8 @@ export async function readSelfHostedNodeConfig(env = process.env) {
|
|
|
1738
1810
|
jobTimeoutMs: parsePositiveInteger(env.MSWARM_SELF_HOSTED_JOB_TIMEOUT_MS, state.job_timeout_ms || DEFAULT_JOB_TIMEOUT_MS),
|
|
1739
1811
|
maxConcurrentJobs,
|
|
1740
1812
|
maxConcurrentLlmJobs,
|
|
1813
|
+
reservedLlmJobs,
|
|
1814
|
+
reservedPriorityMax,
|
|
1741
1815
|
genericJobsEnabled: parseBoolean(env.MSWARM_SELF_HOSTED_GENERIC_JOBS_ENABLED ?? env.MSWARM_SELF_HOSTED_GENERIC_JOBS, state.generic_jobs_enabled === true),
|
|
1742
1816
|
genericJobTimeoutMs: parsePositiveInteger(env.MSWARM_SELF_HOSTED_GENERIC_JOB_TIMEOUT_MS, state.generic_job_timeout_ms || state.job_timeout_ms || DEFAULT_JOB_TIMEOUT_MS),
|
|
1743
1817
|
genericJobMaxConcurrency: parsePositiveInteger(env.MSWARM_SELF_HOSTED_GENERIC_JOB_MAX_CONCURRENCY, state.generic_job_max_concurrency || 1),
|
|
@@ -1787,6 +1861,9 @@ export async function readOwnerSetupConfig(argv = process.argv.slice(3), env = p
|
|
|
1787
1861
|
const packageNodeVersion = await readPackageNodeVersion();
|
|
1788
1862
|
const maxConcurrentJobs = parsePositiveInteger(options["max-concurrent-jobs"] || env.MSWARM_SELF_HOSTED_MAX_CONCURRENT_JOBS, 1);
|
|
1789
1863
|
const maxConcurrentLlmJobs = parsePositiveInteger(options["max-concurrent-llm-jobs"] || env.MSWARM_SELF_HOSTED_MAX_CONCURRENT_LLM_JOBS, maxConcurrentJobs);
|
|
1864
|
+
const reservedLlmJobs = parseNonNegativeInteger(options["reserved-llm-jobs"] ?? env.MSWARM_SELF_HOSTED_RESERVED_LLM_JOBS, 0);
|
|
1865
|
+
const reservedPriorityMax = parseRelayPriority(options["reserved-priority-max"] ?? env.MSWARM_SELF_HOSTED_RESERVED_PRIORITY_MAX, DEFAULT_RESERVED_PRIORITY_MAX);
|
|
1866
|
+
assertReservedLlmCapacity({ maxConcurrentJobs, maxConcurrentLlmJobs, reservedLlmJobs });
|
|
1790
1867
|
return {
|
|
1791
1868
|
apiKey,
|
|
1792
1869
|
handle: handle || undefined,
|
|
@@ -1812,6 +1889,8 @@ export async function readOwnerSetupConfig(argv = process.argv.slice(3), env = p
|
|
|
1812
1889
|
jobTimeoutMs: parsePositiveInteger(options["job-timeout-ms"] || env.MSWARM_SELF_HOSTED_JOB_TIMEOUT_MS, DEFAULT_JOB_TIMEOUT_MS),
|
|
1813
1890
|
maxConcurrentJobs,
|
|
1814
1891
|
maxConcurrentLlmJobs,
|
|
1892
|
+
reservedLlmJobs,
|
|
1893
|
+
reservedPriorityMax,
|
|
1815
1894
|
genericJobsEnabled: parseBoolean(options["enable-generic-jobs"] || env.MSWARM_SELF_HOSTED_GENERIC_JOBS_ENABLED || env.MSWARM_SELF_HOSTED_GENERIC_JOBS, false),
|
|
1816
1895
|
genericJobTimeoutMs: parsePositiveInteger(options["generic-job-timeout-ms"] || env.MSWARM_SELF_HOSTED_GENERIC_JOB_TIMEOUT_MS, DEFAULT_JOB_TIMEOUT_MS),
|
|
1817
1896
|
genericJobMaxConcurrency: parsePositiveInteger(options["generic-job-max-concurrency"] || env.MSWARM_SELF_HOSTED_GENERIC_JOB_MAX_CONCURRENCY, 1),
|
|
@@ -2203,6 +2282,41 @@ function mcodaAgentDefaultModel(agent) {
|
|
|
2203
2282
|
optionalText(agent.models?.find((model) => model.isDefault === true || model.is_default === true)?.model_name) ||
|
|
2204
2283
|
null);
|
|
2205
2284
|
}
|
|
2285
|
+
/**
|
|
2286
|
+
* The agent's base URL, but only when it names a runner on this machine.
|
|
2287
|
+
*
|
|
2288
|
+
* The catalogue publishes a mirror of each self-hosted agent back to the node,
|
|
2289
|
+
* and that mirror's base URL is the gateway. Serving a job from it sends the
|
|
2290
|
+
* request back out instead of to the model running here, so a loopback address
|
|
2291
|
+
* is what distinguishes the real runner from its reflection.
|
|
2292
|
+
*/
|
|
2293
|
+
function mcodaAgentLocalRunnerBaseUrl(agent) {
|
|
2294
|
+
const config = isRecord(agent.config) ? agent.config : {};
|
|
2295
|
+
const localRunner = isRecord(config.localRunner) ? config.localRunner : {};
|
|
2296
|
+
const baseUrl = readConfigString({ ...localRunner, ...config }, [
|
|
2297
|
+
"baseUrl",
|
|
2298
|
+
"base_url",
|
|
2299
|
+
"endpoint",
|
|
2300
|
+
"apiBaseUrl",
|
|
2301
|
+
"api_base_url"
|
|
2302
|
+
]);
|
|
2303
|
+
if (!baseUrl) {
|
|
2304
|
+
return null;
|
|
2305
|
+
}
|
|
2306
|
+
let host;
|
|
2307
|
+
try {
|
|
2308
|
+
host = new URL(baseUrl).hostname.toLowerCase();
|
|
2309
|
+
}
|
|
2310
|
+
catch {
|
|
2311
|
+
return null;
|
|
2312
|
+
}
|
|
2313
|
+
const isLoopback = host === "localhost" ||
|
|
2314
|
+
host === "::1" ||
|
|
2315
|
+
host === "0.0.0.0" ||
|
|
2316
|
+
host.endsWith(".localhost") ||
|
|
2317
|
+
/^127\./.test(host);
|
|
2318
|
+
return isLoopback ? baseUrl : null;
|
|
2319
|
+
}
|
|
2206
2320
|
function resolveCodaliProviderForAgent(agent) {
|
|
2207
2321
|
const adapter = optionalText(agent.adapter);
|
|
2208
2322
|
if (isLocalOpenAiCompatibleAdapter(adapter)) {
|
|
@@ -4100,6 +4214,7 @@ export class MswarmSelfHostedNodeClient {
|
|
|
4100
4214
|
this.jobsResultPathTemplate = lifecyclePath(input.jobsResultPathTemplate, DEFAULT_SELF_HOSTED_JOBS_RESULT_PATH_TEMPLATE);
|
|
4101
4215
|
this.fetchImpl = input.fetchImpl || fetch;
|
|
4102
4216
|
this.timeoutMs = input.timeoutMs || DEFAULT_REQUEST_TIMEOUT_MS;
|
|
4217
|
+
this.resultTimeoutMs = Math.max(this.timeoutMs, input.resultTimeoutMs || DEFAULT_RESULT_POST_TIMEOUT_MS);
|
|
4103
4218
|
}
|
|
4104
4219
|
lifecycleEndpoint(kind) {
|
|
4105
4220
|
const path = kind === "poll"
|
|
@@ -4214,7 +4329,7 @@ export class MswarmSelfHostedNodeClient {
|
|
|
4214
4329
|
authorization: `Bearer ${runtimeToken}`
|
|
4215
4330
|
},
|
|
4216
4331
|
body: JSON.stringify(payload)
|
|
4217
|
-
}, this.
|
|
4332
|
+
}, this.resultTimeoutMs);
|
|
4218
4333
|
}
|
|
4219
4334
|
async postJobStart(runtimeToken, jobId, payload) {
|
|
4220
4335
|
return fetchJson(this.fetchImpl, this.lifecycleUrl("start", jobId), {
|
|
@@ -4246,6 +4361,12 @@ export class SelfHostedNodeRuntime {
|
|
|
4246
4361
|
this.latencySamplesMs = [];
|
|
4247
4362
|
this.recentFailures = [];
|
|
4248
4363
|
this.lifecyclePollingDisabled = false;
|
|
4364
|
+
assertReservedLlmCapacity({
|
|
4365
|
+
maxConcurrentJobs: Math.max(1, Math.floor(config.maxConcurrentJobs || 1)),
|
|
4366
|
+
maxConcurrentLlmJobs: Math.max(1, Math.floor(config.maxConcurrentLlmJobs || config.maxConcurrentJobs || 1)),
|
|
4367
|
+
reservedLlmJobs: parseNonNegativeInteger(config.reservedLlmJobs, 0)
|
|
4368
|
+
});
|
|
4369
|
+
parseRelayPriority(config.reservedPriorityMax, DEFAULT_RESERVED_PRIORITY_MAX);
|
|
4249
4370
|
this.config = config;
|
|
4250
4371
|
this.fetchImpl = deps?.fetchImpl || fetch;
|
|
4251
4372
|
this.useDedicatedGenerativeDispatcher = deps?.fetchImpl === undefined;
|
|
@@ -4258,7 +4379,8 @@ export class SelfHostedNodeRuntime {
|
|
|
4258
4379
|
jobsEventsPathTemplate: config.jobsEventsPathTemplate,
|
|
4259
4380
|
jobsResultPathTemplate: config.jobsResultPathTemplate,
|
|
4260
4381
|
fetchImpl: this.fetchImpl,
|
|
4261
|
-
timeoutMs: config.requestTimeoutMs
|
|
4382
|
+
timeoutMs: config.requestTimeoutMs,
|
|
4383
|
+
resultTimeoutMs: Math.min(config.jobTimeoutMs, DEFAULT_RESULT_POST_TIMEOUT_MS)
|
|
4262
4384
|
});
|
|
4263
4385
|
this.mcoda =
|
|
4264
4386
|
deps?.mcoda ||
|
|
@@ -4448,7 +4570,8 @@ export class SelfHostedNodeRuntime {
|
|
|
4448
4570
|
new MswarmSelfHostedNodeClient({
|
|
4449
4571
|
gatewayBaseUrl: setupConfig.gatewayBaseUrl,
|
|
4450
4572
|
fetchImpl: deps?.fetchImpl,
|
|
4451
|
-
timeoutMs: setupConfig.requestTimeoutMs
|
|
4573
|
+
timeoutMs: setupConfig.requestTimeoutMs,
|
|
4574
|
+
resultTimeoutMs: Math.min(setupConfig.jobTimeoutMs, DEFAULT_RESULT_POST_TIMEOUT_MS)
|
|
4452
4575
|
});
|
|
4453
4576
|
const machineId = await readOrCreateSelfHostedMachineId(setupConfig.machineIdPath);
|
|
4454
4577
|
const machineFingerprint = machineFingerprintFromId(machineId);
|
|
@@ -4517,6 +4640,8 @@ export class SelfHostedNodeRuntime {
|
|
|
4517
4640
|
job_timeout_ms: setupConfig.jobTimeoutMs,
|
|
4518
4641
|
max_concurrent_jobs: setupConfig.maxConcurrentJobs,
|
|
4519
4642
|
max_concurrent_llm_jobs: setupConfig.maxConcurrentLlmJobs,
|
|
4643
|
+
reserved_llm_jobs: setupConfig.reservedLlmJobs,
|
|
4644
|
+
reserved_priority_max: setupConfig.reservedPriorityMax,
|
|
4520
4645
|
generic_jobs_enabled: setupConfig.genericJobsEnabled,
|
|
4521
4646
|
generic_job_timeout_ms: setupConfig.genericJobTimeoutMs,
|
|
4522
4647
|
generic_job_max_concurrency: setupConfig.genericJobMaxConcurrency,
|
|
@@ -4541,7 +4666,8 @@ export class SelfHostedNodeRuntime {
|
|
|
4541
4666
|
jobsEventsPathTemplate,
|
|
4542
4667
|
jobsResultPathTemplate,
|
|
4543
4668
|
fetchImpl: deps?.fetchImpl,
|
|
4544
|
-
timeoutMs: setupConfig.requestTimeoutMs
|
|
4669
|
+
timeoutMs: setupConfig.requestTimeoutMs,
|
|
4670
|
+
resultTimeoutMs: Math.min(setupConfig.jobTimeoutMs, DEFAULT_RESULT_POST_TIMEOUT_MS)
|
|
4545
4671
|
});
|
|
4546
4672
|
const runtime = new SelfHostedNodeRuntime({
|
|
4547
4673
|
gatewayBaseUrl: trimTrailingSlash(relayGatewayBaseUrl),
|
|
@@ -4572,6 +4698,8 @@ export class SelfHostedNodeRuntime {
|
|
|
4572
4698
|
jobTimeoutMs: setupConfig.jobTimeoutMs,
|
|
4573
4699
|
maxConcurrentJobs: setupConfig.maxConcurrentJobs,
|
|
4574
4700
|
maxConcurrentLlmJobs: setupConfig.maxConcurrentLlmJobs,
|
|
4701
|
+
reservedLlmJobs: setupConfig.reservedLlmJobs,
|
|
4702
|
+
reservedPriorityMax: setupConfig.reservedPriorityMax,
|
|
4575
4703
|
genericJobsEnabled: setupConfig.genericJobsEnabled,
|
|
4576
4704
|
genericJobTimeoutMs: setupConfig.genericJobTimeoutMs,
|
|
4577
4705
|
genericJobMaxConcurrency: setupConfig.genericJobMaxConcurrency,
|
|
@@ -4702,6 +4830,8 @@ export class SelfHostedNodeRuntime {
|
|
|
4702
4830
|
job_timeout_ms: this.config.jobTimeoutMs,
|
|
4703
4831
|
max_concurrent_jobs: this.config.maxConcurrentJobs,
|
|
4704
4832
|
max_concurrent_llm_jobs: this.config.maxConcurrentLlmJobs,
|
|
4833
|
+
reserved_llm_jobs: this.config.reservedLlmJobs ?? 0,
|
|
4834
|
+
reserved_priority_max: this.config.reservedPriorityMax ?? DEFAULT_RESERVED_PRIORITY_MAX,
|
|
4705
4835
|
generic_jobs_enabled: this.config.genericJobsEnabled,
|
|
4706
4836
|
generic_job_timeout_ms: this.config.genericJobTimeoutMs,
|
|
4707
4837
|
generic_job_max_concurrency: this.config.genericJobMaxConcurrency,
|
|
@@ -4730,7 +4860,16 @@ export class SelfHostedNodeRuntime {
|
|
|
4730
4860
|
async executeOpenAiToolPassthrough(job, agent) {
|
|
4731
4861
|
const baseUrl = optionalText(agent.baseUrl);
|
|
4732
4862
|
if (!baseUrl) {
|
|
4733
|
-
|
|
4863
|
+
/*
|
|
4864
|
+
* Name the agent. "agent has no local runner base URL" alone cannot be
|
|
4865
|
+
* acted on: a node holds several entries per model - the local runner and
|
|
4866
|
+
* the gateway's mirror of it - and this says nothing about which one was
|
|
4867
|
+
* resolved or whether it carried a config at all.
|
|
4868
|
+
*/
|
|
4869
|
+
throw new SelfHostedPreStartJobError("selected_agent_unavailable", `agent has no local runner base URL for OpenAI tool pass-through ` +
|
|
4870
|
+
`(slug=${optionalText(agent.slug) || "?"} adapter=${optionalText(agent.adapter) || "?"} ` +
|
|
4871
|
+
`runnerKind=${optionalText(agent.runnerKind) || "?"} ` +
|
|
4872
|
+
`localRunner=${agent.localRunner ? "yes" : "no"} model=${optionalText(agent.model) || "?"})`);
|
|
4734
4873
|
}
|
|
4735
4874
|
const headers = { "content-type": "application/json" };
|
|
4736
4875
|
const bearer = agent.authMode === "bearer"
|
|
@@ -5123,7 +5262,7 @@ export class SelfHostedNodeRuntime {
|
|
|
5123
5262
|
}
|
|
5124
5263
|
const rawAgents = await this.mcoda.listRawAgents();
|
|
5125
5264
|
const strictSelectedAgent = selectedSourceAgentSlug || selectedAgentSlug;
|
|
5126
|
-
const
|
|
5265
|
+
const matches = rawAgents.filter((entry) => {
|
|
5127
5266
|
const slug = optionalText(entry.slug);
|
|
5128
5267
|
if (strictSelectedAgent) {
|
|
5129
5268
|
return slug === strictSelectedAgent;
|
|
@@ -5131,6 +5270,15 @@ export class SelfHostedNodeRuntime {
|
|
|
5131
5270
|
const defaultModel = mcodaAgentDefaultModel(entry);
|
|
5132
5271
|
return slug === selected || defaultModel === selected;
|
|
5133
5272
|
});
|
|
5273
|
+
/*
|
|
5274
|
+
* A node carries two entries for the same model: the local runner, and the
|
|
5275
|
+
* gateway mirror that the catalogue publishes back to it. The mirror's base
|
|
5276
|
+
* URL is the gateway itself, so serving a job from it sends the request back
|
|
5277
|
+
* out to the gateway instead of to the model sitting on this machine - which
|
|
5278
|
+
* is why OpenAI tool pass-through reported having no local runner to call.
|
|
5279
|
+
* Take the entry that actually points at a runner here.
|
|
5280
|
+
*/
|
|
5281
|
+
const agent = matches.find((entry) => Boolean(mcodaAgentLocalRunnerBaseUrl(entry))) ?? matches[0];
|
|
5134
5282
|
if (!agent) {
|
|
5135
5283
|
throw new SelfHostedPreStartJobError("selected_agent_unavailable", `selected local mcoda agent ${selected} is not available on this node`);
|
|
5136
5284
|
}
|
|
@@ -5853,11 +6001,11 @@ export class SelfHostedNodeRuntime {
|
|
|
5853
6001
|
});
|
|
5854
6002
|
return { count: models.filter((model) => model.exposed !== false).length, response };
|
|
5855
6003
|
}
|
|
5856
|
-
async
|
|
6004
|
+
async pollRelayJob(waitMs = DEFAULT_JOB_POLL_WAIT_MS, options = {}) {
|
|
5857
6005
|
const lifecycleState = await this.readLifecycleState();
|
|
5858
6006
|
if (this.lifecyclePollingDisabled || this.isLifecycleProtocolDegradedState(lifecycleState)) {
|
|
5859
6007
|
this.lifecyclePollingDisabled = true;
|
|
5860
|
-
return {
|
|
6008
|
+
return { claim: null, status: "failed" };
|
|
5861
6009
|
}
|
|
5862
6010
|
const enrollment = await this.ensureEnrolled();
|
|
5863
6011
|
const pollCapacity = this.buildLoadTelemetry({ models: [] });
|
|
@@ -5871,7 +6019,17 @@ export class SelfHostedNodeRuntime {
|
|
|
5871
6019
|
max_jobs: pollCapacity.max_concurrency,
|
|
5872
6020
|
max_concurrency: pollCapacity.max_concurrency,
|
|
5873
6021
|
free_slots: pollCapacity.free_slots,
|
|
5874
|
-
drain_mode: pollCapacity.drain_mode
|
|
6022
|
+
drain_mode: pollCapacity.drain_mode,
|
|
6023
|
+
...(options.excludedAgentSlugs !== undefined
|
|
6024
|
+
? {
|
|
6025
|
+
excluded_agent_slugs: boundedRelayAgentAliases([
|
|
6026
|
+
options.excludedAgentSlugs
|
|
6027
|
+
])
|
|
6028
|
+
}
|
|
6029
|
+
: {}),
|
|
6030
|
+
...(options.maxRequestedPriority !== undefined
|
|
6031
|
+
? { max_requested_priority: options.maxRequestedPriority }
|
|
6032
|
+
: {})
|
|
5875
6033
|
},
|
|
5876
6034
|
wait_ms: waitMs
|
|
5877
6035
|
});
|
|
@@ -5880,14 +6038,24 @@ export class SelfHostedNodeRuntime {
|
|
|
5880
6038
|
const mismatch = this.lifecycleProtocolMismatch("poll", error);
|
|
5881
6039
|
if (mismatch) {
|
|
5882
6040
|
await this.markLifecycleProtocolDegraded(mismatch);
|
|
5883
|
-
return {
|
|
6041
|
+
return { claim: null, status: "failed" };
|
|
5884
6042
|
}
|
|
5885
6043
|
throw error;
|
|
5886
6044
|
}
|
|
5887
6045
|
const job = response.job || null;
|
|
5888
6046
|
if (!job) {
|
|
5889
|
-
return {
|
|
6047
|
+
return { claim: null };
|
|
5890
6048
|
}
|
|
6049
|
+
return {
|
|
6050
|
+
claim: {
|
|
6051
|
+
runtimeToken: enrollment.runtimeToken,
|
|
6052
|
+
job,
|
|
6053
|
+
attachedMswarmApiKey: optionalText(response.attached_mswarm_api_key) || undefined
|
|
6054
|
+
}
|
|
6055
|
+
};
|
|
6056
|
+
}
|
|
6057
|
+
async executeRelayJobClaim(claim) {
|
|
6058
|
+
const { job, runtimeToken } = claim;
|
|
5891
6059
|
const chatStreaming = (normalizeSelfHostedGenerativeOperationName(job.operation) ?? "chat.completions") ===
|
|
5892
6060
|
"chat.completions" &&
|
|
5893
6061
|
job.openai_request.stream === true;
|
|
@@ -5900,7 +6068,7 @@ export class SelfHostedNodeRuntime {
|
|
|
5900
6068
|
}
|
|
5901
6069
|
const stream_events = pendingStreamEvents.splice(0, pendingStreamEvents.length);
|
|
5902
6070
|
try {
|
|
5903
|
-
await this.gateway.postJobEvents(
|
|
6071
|
+
await this.gateway.postJobEvents(runtimeToken, job.job_id, {
|
|
5904
6072
|
node_id: this.config.nodeId,
|
|
5905
6073
|
stream_events
|
|
5906
6074
|
});
|
|
@@ -5917,10 +6085,10 @@ export class SelfHostedNodeRuntime {
|
|
|
5917
6085
|
}
|
|
5918
6086
|
};
|
|
5919
6087
|
let result = await this.executeJob(job, {
|
|
5920
|
-
attachedMswarmApiKey:
|
|
6088
|
+
attachedMswarmApiKey: claim.attachedMswarmApiKey,
|
|
5921
6089
|
onStarted: async (event) => {
|
|
5922
6090
|
try {
|
|
5923
|
-
await this.gateway.postJobStart(
|
|
6091
|
+
await this.gateway.postJobStart(runtimeToken, job.job_id, {
|
|
5924
6092
|
node_id: this.config.nodeId,
|
|
5925
6093
|
agent_slug: event.agent_slug || job.agent_slug,
|
|
5926
6094
|
source_agent_slug: event.source_agent_slug || job.source_agent_slug || null,
|
|
@@ -5975,7 +6143,7 @@ export class SelfHostedNodeRuntime {
|
|
|
5975
6143
|
let resultPostError = null;
|
|
5976
6144
|
for (let attempt = 0; attempt <= RELAY_RESULT_POST_RETRY_DELAYS_MS.length; attempt += 1) {
|
|
5977
6145
|
try {
|
|
5978
|
-
await this.gateway.postJobResult(
|
|
6146
|
+
await this.gateway.postJobResult(runtimeToken, job.job_id, this.jobResultPayload(postedResult));
|
|
5979
6147
|
resultPostError = null;
|
|
5980
6148
|
break;
|
|
5981
6149
|
}
|
|
@@ -6003,6 +6171,13 @@ export class SelfHostedNodeRuntime {
|
|
|
6003
6171
|
}
|
|
6004
6172
|
return { executed: true, job_id: job.job_id, status: result.status };
|
|
6005
6173
|
}
|
|
6174
|
+
async pollAndExecuteJob(waitMs = DEFAULT_JOB_POLL_WAIT_MS) {
|
|
6175
|
+
const polled = await this.pollRelayJob(waitMs);
|
|
6176
|
+
if (!polled.claim) {
|
|
6177
|
+
return { executed: false, ...(polled.status ? { status: polled.status } : {}) };
|
|
6178
|
+
}
|
|
6179
|
+
return this.executeRelayJobClaim(polled.claim);
|
|
6180
|
+
}
|
|
6006
6181
|
async doctor() {
|
|
6007
6182
|
const checks = [];
|
|
6008
6183
|
checks.push({ name: "config", ok: Boolean(this.config.nodeId), message: this.config.nodeId || "missing node id" });
|
|
@@ -6041,19 +6216,33 @@ export class SelfHostedNodeRuntime {
|
|
|
6041
6216
|
let stopped = false;
|
|
6042
6217
|
let timer = null;
|
|
6043
6218
|
let pollTimer = null;
|
|
6219
|
+
let pollTimerDueAt = 0;
|
|
6044
6220
|
let polling = false;
|
|
6045
|
-
let
|
|
6221
|
+
let relayPollFailureStreak = 0;
|
|
6222
|
+
let relayExecutionFailureStreak = 0;
|
|
6223
|
+
let activeRelayExecutions = 0;
|
|
6224
|
+
let activeRegularRelayExecutions = 0;
|
|
6225
|
+
let relayExecutionSequence = 0;
|
|
6226
|
+
const activeRelayAgentAliases = new Map();
|
|
6046
6227
|
let revoked = false;
|
|
6228
|
+
const regularRelayExecutionLimit = Math.max(1, Math.floor(this.config.maxConcurrentLlmJobs || this.config.maxConcurrentJobs || 1));
|
|
6229
|
+
const reservedRelayExecutionLimit = parseNonNegativeInteger(this.config.reservedLlmJobs, 0);
|
|
6230
|
+
const reservedPriorityMax = parseRelayPriority(this.config.reservedPriorityMax, DEFAULT_RESERVED_PRIORITY_MAX);
|
|
6231
|
+
const relayExecutionLimit = regularRelayExecutionLimit + reservedRelayExecutionLimit;
|
|
6232
|
+
const clearPollTimer = () => {
|
|
6233
|
+
if (pollTimer) {
|
|
6234
|
+
clearTimeout(pollTimer);
|
|
6235
|
+
pollTimer = null;
|
|
6236
|
+
}
|
|
6237
|
+
pollTimerDueAt = 0;
|
|
6238
|
+
};
|
|
6047
6239
|
const halt = () => {
|
|
6048
6240
|
stopped = true;
|
|
6049
6241
|
if (timer) {
|
|
6050
6242
|
clearTimeout(timer);
|
|
6051
6243
|
timer = null;
|
|
6052
6244
|
}
|
|
6053
|
-
|
|
6054
|
-
clearTimeout(pollTimer);
|
|
6055
|
-
pollTimer = null;
|
|
6056
|
-
}
|
|
6245
|
+
clearPollTimer();
|
|
6057
6246
|
};
|
|
6058
6247
|
// 410 means the gateway has retired this node's credentials: it was removed there.
|
|
6059
6248
|
// Retrying at full rate achieves nothing but a hot loop and a flooded journal, so
|
|
@@ -6067,10 +6256,7 @@ export class SelfHostedNodeRuntime {
|
|
|
6067
6256
|
if (!isHttpStatusError(error, 410)) {
|
|
6068
6257
|
return false;
|
|
6069
6258
|
}
|
|
6070
|
-
|
|
6071
|
-
clearTimeout(pollTimer);
|
|
6072
|
-
pollTimer = null;
|
|
6073
|
-
}
|
|
6259
|
+
clearPollTimer();
|
|
6074
6260
|
if (!revoked) {
|
|
6075
6261
|
revoked = true;
|
|
6076
6262
|
console.error(`[mswarm] this node was removed at the gateway (node ${this.config.nodeId}). ` +
|
|
@@ -6081,42 +6267,112 @@ export class SelfHostedNodeRuntime {
|
|
|
6081
6267
|
return true;
|
|
6082
6268
|
};
|
|
6083
6269
|
const noteAlive = () => {
|
|
6084
|
-
|
|
6270
|
+
const recoveredFromRevocation = revoked;
|
|
6271
|
+
const relayPollingIsBackedOff = (relayPollFailureStreak > 0 || relayExecutionFailureStreak > 0) && !polling;
|
|
6272
|
+
if (!recoveredFromRevocation && !relayPollingIsBackedOff) {
|
|
6085
6273
|
return;
|
|
6086
6274
|
}
|
|
6087
|
-
|
|
6088
|
-
|
|
6089
|
-
|
|
6275
|
+
if (recoveredFromRevocation) {
|
|
6276
|
+
revoked = false;
|
|
6277
|
+
console.error(`[mswarm] node ${this.config.nodeId} is accepted at the gateway again; resuming normal operation.`);
|
|
6278
|
+
}
|
|
6279
|
+
clearPollTimer();
|
|
6280
|
+
relayPollFailureStreak = 0;
|
|
6281
|
+
relayExecutionFailureStreak = 0;
|
|
6282
|
+
schedulePoll(0);
|
|
6283
|
+
};
|
|
6284
|
+
const currentRelayRetryDelayMs = () => Math.max(pollRetryDelayMs(relayPollFailureStreak), pollRetryDelayMs(relayExecutionFailureStreak));
|
|
6285
|
+
const schedulePoll = (delayMs) => {
|
|
6286
|
+
if (stopped ||
|
|
6287
|
+
polling ||
|
|
6288
|
+
revoked ||
|
|
6289
|
+
this.lifecyclePollingDisabled ||
|
|
6290
|
+
this.config.relayMode === "direct" ||
|
|
6291
|
+
activeRelayExecutions >= relayExecutionLimit) {
|
|
6292
|
+
return;
|
|
6293
|
+
}
|
|
6294
|
+
const dueAt = Date.now() + Math.max(0, delayMs);
|
|
6295
|
+
if (pollTimer && pollTimerDueAt >= dueAt) {
|
|
6296
|
+
return;
|
|
6297
|
+
}
|
|
6298
|
+
clearPollTimer();
|
|
6299
|
+
pollTimerDueAt = dueAt;
|
|
6300
|
+
pollTimer = setTimeout(() => {
|
|
6301
|
+
pollTimer = null;
|
|
6302
|
+
pollTimerDueAt = 0;
|
|
6303
|
+
poll();
|
|
6304
|
+
}, Math.max(0, dueAt - Date.now()));
|
|
6090
6305
|
};
|
|
6091
6306
|
const poll = () => {
|
|
6092
6307
|
if (stopped ||
|
|
6093
6308
|
polling ||
|
|
6094
6309
|
revoked ||
|
|
6095
6310
|
this.lifecyclePollingDisabled ||
|
|
6096
|
-
this.config.relayMode === "direct"
|
|
6311
|
+
this.config.relayMode === "direct" ||
|
|
6312
|
+
activeRelayExecutions >= relayExecutionLimit) {
|
|
6097
6313
|
return;
|
|
6098
6314
|
}
|
|
6099
6315
|
polling = true;
|
|
6100
|
-
|
|
6101
|
-
|
|
6102
|
-
|
|
6316
|
+
const reservedOnly = reservedRelayExecutionLimit > 0 &&
|
|
6317
|
+
activeRegularRelayExecutions >= regularRelayExecutionLimit &&
|
|
6318
|
+
activeRelayExecutions < relayExecutionLimit;
|
|
6319
|
+
const excludedAgentSlugs = boundedRelayAgentAliases(activeRelayAgentAliases.values());
|
|
6320
|
+
void this.pollRelayJob(reservedOnly ? RESERVED_JOB_POLL_WAIT_MS : DEFAULT_JOB_POLL_WAIT_MS, {
|
|
6321
|
+
...(reservedRelayExecutionLimit > 0 ? { excludedAgentSlugs } : {}),
|
|
6322
|
+
...(reservedOnly ? { maxRequestedPriority: reservedPriorityMax } : {})
|
|
6323
|
+
})
|
|
6324
|
+
.then((polled) => {
|
|
6325
|
+
relayPollFailureStreak = 0;
|
|
6326
|
+
const claim = polled.claim;
|
|
6327
|
+
if (!claim) {
|
|
6328
|
+
return;
|
|
6329
|
+
}
|
|
6330
|
+
const regularLane = !reservedOnly;
|
|
6331
|
+
const executionId = ++relayExecutionSequence;
|
|
6332
|
+
activeRelayExecutions += 1;
|
|
6333
|
+
if (regularLane) {
|
|
6334
|
+
activeRegularRelayExecutions += 1;
|
|
6335
|
+
}
|
|
6336
|
+
activeRelayAgentAliases.set(executionId, relayJobAgentAliases(claim.job));
|
|
6337
|
+
void this.executeRelayJobClaim(claim)
|
|
6338
|
+
.then(() => {
|
|
6339
|
+
relayExecutionFailureStreak = 0;
|
|
6340
|
+
})
|
|
6341
|
+
.catch((error) => {
|
|
6342
|
+
if (noteRevoked(error)) {
|
|
6343
|
+
return;
|
|
6344
|
+
}
|
|
6345
|
+
relayExecutionFailureStreak += 1;
|
|
6346
|
+
console.error(`[mswarm] self-hosted relay job ${claim.job.job_id} failed: ${runtimeErrorMessage(error)}`);
|
|
6347
|
+
})
|
|
6348
|
+
.finally(() => {
|
|
6349
|
+
activeRelayExecutions = Math.max(0, activeRelayExecutions - 1);
|
|
6350
|
+
if (regularLane) {
|
|
6351
|
+
activeRegularRelayExecutions = Math.max(0, activeRegularRelayExecutions - 1);
|
|
6352
|
+
}
|
|
6353
|
+
activeRelayAgentAliases.delete(executionId);
|
|
6354
|
+
schedulePoll(currentRelayRetryDelayMs());
|
|
6355
|
+
});
|
|
6103
6356
|
})
|
|
6104
6357
|
.catch((error) => {
|
|
6105
6358
|
if (noteRevoked(error)) {
|
|
6106
6359
|
return;
|
|
6107
6360
|
}
|
|
6108
|
-
|
|
6361
|
+
relayPollFailureStreak += 1;
|
|
6109
6362
|
console.error(`[mswarm] self-hosted relay poll failed: ${runtimeErrorMessage(error)}`);
|
|
6110
6363
|
})
|
|
6111
6364
|
.finally(() => {
|
|
6112
6365
|
polling = false;
|
|
6113
|
-
if (stopped ||
|
|
6366
|
+
if (stopped ||
|
|
6367
|
+
revoked ||
|
|
6368
|
+
this.lifecyclePollingDisabled ||
|
|
6369
|
+
activeRelayExecutions >= relayExecutionLimit) {
|
|
6114
6370
|
return;
|
|
6115
6371
|
}
|
|
6116
6372
|
// A healthy poll long-polls at the gateway, so re-entering immediately is
|
|
6117
6373
|
// correct. A failing one returns at once, so without this the loop spins as
|
|
6118
6374
|
// fast as the network allows.
|
|
6119
|
-
|
|
6375
|
+
schedulePoll(currentRelayRetryDelayMs());
|
|
6120
6376
|
});
|
|
6121
6377
|
};
|
|
6122
6378
|
const schedule = () => {
|
|
@@ -6138,7 +6394,7 @@ export class SelfHostedNodeRuntime {
|
|
|
6138
6394
|
})
|
|
6139
6395
|
.finally(() => {
|
|
6140
6396
|
schedule();
|
|
6141
|
-
|
|
6397
|
+
schedulePoll(0);
|
|
6142
6398
|
});
|
|
6143
6399
|
return { stop: halt };
|
|
6144
6400
|
}
|