@desplega.ai/agent-swarm 1.67.5 → 1.68.0
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/openapi.json +64 -1
- package/package.json +1 -1
- package/src/http/config.ts +67 -0
- package/src/http/core.ts +39 -29
package/openapi.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"openapi": "3.1.0",
|
|
3
3
|
"info": {
|
|
4
4
|
"title": "Agent Swarm API",
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.68.0",
|
|
6
6
|
"description": "Multi-agent orchestration API for Claude Code, Codex, and Gemini CLI. Enables task distribution, agent communication, and service discovery.\n\nMCP tools are documented separately in [MCP.md](./MCP.md)."
|
|
7
7
|
},
|
|
8
8
|
"servers": [
|
|
@@ -995,6 +995,69 @@
|
|
|
995
995
|
}
|
|
996
996
|
}
|
|
997
997
|
},
|
|
998
|
+
"/api/config/env-presence": {
|
|
999
|
+
"get": {
|
|
1000
|
+
"summary": "Check which of the given env var keys are currently set in process.env (presence only, no values)",
|
|
1001
|
+
"tags": [
|
|
1002
|
+
"Config"
|
|
1003
|
+
],
|
|
1004
|
+
"security": [
|
|
1005
|
+
{
|
|
1006
|
+
"bearerAuth": []
|
|
1007
|
+
}
|
|
1008
|
+
],
|
|
1009
|
+
"parameters": [
|
|
1010
|
+
{
|
|
1011
|
+
"schema": {
|
|
1012
|
+
"type": "string",
|
|
1013
|
+
"minLength": 1
|
|
1014
|
+
},
|
|
1015
|
+
"required": true,
|
|
1016
|
+
"name": "keys",
|
|
1017
|
+
"in": "query"
|
|
1018
|
+
}
|
|
1019
|
+
],
|
|
1020
|
+
"responses": {
|
|
1021
|
+
"200": {
|
|
1022
|
+
"description": "Map of key -> boolean (true iff set in process.env)"
|
|
1023
|
+
},
|
|
1024
|
+
"400": {
|
|
1025
|
+
"description": "Validation error"
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
},
|
|
1030
|
+
"/api/config/reload": {
|
|
1031
|
+
"post": {
|
|
1032
|
+
"summary": "Reload global swarm_config into process.env (override=true) and re-init integrations (Slack, GitHub, Linear, AgentMail)",
|
|
1033
|
+
"tags": [
|
|
1034
|
+
"Config"
|
|
1035
|
+
],
|
|
1036
|
+
"security": [
|
|
1037
|
+
{
|
|
1038
|
+
"bearerAuth": []
|
|
1039
|
+
}
|
|
1040
|
+
],
|
|
1041
|
+
"requestBody": {
|
|
1042
|
+
"content": {
|
|
1043
|
+
"application/json": {
|
|
1044
|
+
"schema": {
|
|
1045
|
+
"type": "object",
|
|
1046
|
+
"properties": {}
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
},
|
|
1051
|
+
"responses": {
|
|
1052
|
+
"200": {
|
|
1053
|
+
"description": "Reload result"
|
|
1054
|
+
},
|
|
1055
|
+
"500": {
|
|
1056
|
+
"description": "Reload failed"
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
},
|
|
998
1061
|
"/api/config/{id}": {
|
|
999
1062
|
"get": {
|
|
1000
1063
|
"summary": "Get a single config entry by ID",
|
package/package.json
CHANGED
package/src/http/config.ts
CHANGED
|
@@ -10,9 +10,12 @@ import {
|
|
|
10
10
|
upsertSwarmConfig,
|
|
11
11
|
} from "../be/db";
|
|
12
12
|
import { isReservedConfigKey, reservedKeyError } from "../be/swarm-config-guard";
|
|
13
|
+
import { reloadGlobalConfigsAndIntegrations } from "./core";
|
|
13
14
|
import { route } from "./route-def";
|
|
14
15
|
import { json, jsonError } from "./utils";
|
|
15
16
|
|
|
17
|
+
const MAX_ENV_PRESENCE_KEYS = 200;
|
|
18
|
+
|
|
16
19
|
// ─── Route Definitions ───────────────────────────────────────────────────────
|
|
17
20
|
|
|
18
21
|
const getResolvedConfigRoute = route({
|
|
@@ -31,6 +34,36 @@ const getResolvedConfigRoute = route({
|
|
|
31
34
|
},
|
|
32
35
|
});
|
|
33
36
|
|
|
37
|
+
const envPresence = route({
|
|
38
|
+
method: "get",
|
|
39
|
+
path: "/api/config/env-presence",
|
|
40
|
+
pattern: ["api", "config", "env-presence"],
|
|
41
|
+
summary:
|
|
42
|
+
"Check which of the given env var keys are currently set in process.env (presence only, no values)",
|
|
43
|
+
tags: ["Config"],
|
|
44
|
+
query: z.object({
|
|
45
|
+
keys: z.string().min(1),
|
|
46
|
+
}),
|
|
47
|
+
responses: {
|
|
48
|
+
200: { description: "Map of key -> boolean (true iff set in process.env)" },
|
|
49
|
+
400: { description: "Validation error" },
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const reloadConfigRoute = route({
|
|
54
|
+
method: "post",
|
|
55
|
+
path: "/api/config/reload",
|
|
56
|
+
pattern: ["api", "config", "reload"],
|
|
57
|
+
summary:
|
|
58
|
+
"Reload global swarm_config into process.env (override=true) and re-init integrations (Slack, GitHub, Linear, AgentMail)",
|
|
59
|
+
tags: ["Config"],
|
|
60
|
+
body: z.object({}).optional(),
|
|
61
|
+
responses: {
|
|
62
|
+
200: { description: "Reload result" },
|
|
63
|
+
500: { description: "Reload failed" },
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
|
|
34
67
|
const getConfigById = route({
|
|
35
68
|
method: "get",
|
|
36
69
|
path: "/api/config/{id}",
|
|
@@ -117,6 +150,40 @@ export async function handleConfig(
|
|
|
117
150
|
return true;
|
|
118
151
|
}
|
|
119
152
|
|
|
153
|
+
if (envPresence.match(req.method, pathSegments)) {
|
|
154
|
+
const parsed = await envPresence.parse(req, res, pathSegments, queryParams);
|
|
155
|
+
if (!parsed) return true;
|
|
156
|
+
const keys = parsed.query.keys
|
|
157
|
+
.split(",")
|
|
158
|
+
.map((k) => k.trim())
|
|
159
|
+
.filter(Boolean);
|
|
160
|
+
if (keys.length > MAX_ENV_PRESENCE_KEYS) {
|
|
161
|
+
jsonError(res, `Too many keys (max ${MAX_ENV_PRESENCE_KEYS})`, 400);
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
const presence: Record<string, boolean> = {};
|
|
165
|
+
for (const key of keys) {
|
|
166
|
+
presence[key] = process.env[key] !== undefined && process.env[key] !== "";
|
|
167
|
+
}
|
|
168
|
+
json(res, { presence });
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (reloadConfigRoute.match(req.method, pathSegments)) {
|
|
173
|
+
try {
|
|
174
|
+
const result = await reloadGlobalConfigsAndIntegrations();
|
|
175
|
+
console.log(
|
|
176
|
+
`[reload-config] Loaded ${result.configsLoaded} config(s), re-initialized: ${result.integrationsReinitialized.join(", ") || "none"}`,
|
|
177
|
+
);
|
|
178
|
+
json(res, { success: true, ...result });
|
|
179
|
+
} catch (e) {
|
|
180
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
181
|
+
console.error("[reload-config] Failed:", message);
|
|
182
|
+
jsonError(res, `Failed to reload config: ${message}`, 500);
|
|
183
|
+
}
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
|
|
120
187
|
if (getConfigById.match(req.method, pathSegments)) {
|
|
121
188
|
const parsed = await getConfigById.parse(req, res, pathSegments, queryParams);
|
|
122
189
|
if (!parsed) return true;
|
package/src/http/core.ts
CHANGED
|
@@ -43,6 +43,42 @@ export function loadGlobalConfigsIntoEnv(override = false): string[] {
|
|
|
43
43
|
return updated;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
export type ReloadConfigResult = {
|
|
47
|
+
configsLoaded: number;
|
|
48
|
+
keysUpdated: string[];
|
|
49
|
+
integrationsReinitialized: string[];
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Re-read swarm_config into process.env with override=true, then reset and
|
|
54
|
+
* re-init each integration so long-lived clients (Slack socket mode, etc.)
|
|
55
|
+
* pick up the new values without requiring a process restart.
|
|
56
|
+
*/
|
|
57
|
+
export async function reloadGlobalConfigsAndIntegrations(): Promise<ReloadConfigResult> {
|
|
58
|
+
const updated = loadGlobalConfigsIntoEnv(true);
|
|
59
|
+
|
|
60
|
+
const integrations: string[] = [];
|
|
61
|
+
|
|
62
|
+
resetAgentMail();
|
|
63
|
+
if (initAgentMail()) integrations.push("agentmail");
|
|
64
|
+
|
|
65
|
+
resetGitHub();
|
|
66
|
+
if (initGitHub()) integrations.push("github");
|
|
67
|
+
|
|
68
|
+
resetLinear();
|
|
69
|
+
if (initLinear()) integrations.push("linear");
|
|
70
|
+
|
|
71
|
+
await stopSlackApp();
|
|
72
|
+
await startSlackApp();
|
|
73
|
+
integrations.push("slack");
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
configsLoaded: updated.length,
|
|
77
|
+
keysUpdated: updated,
|
|
78
|
+
integrationsReinitialized: integrations,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
46
82
|
export async function handleCore(
|
|
47
83
|
req: IncomingMessage,
|
|
48
84
|
res: ServerResponse,
|
|
@@ -116,38 +152,12 @@ export async function handleCore(
|
|
|
116
152
|
// POST /internal/reload-config — re-read swarm_config into process.env and re-init integrations
|
|
117
153
|
if (req.method === "POST" && req.url === "/internal/reload-config") {
|
|
118
154
|
try {
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
// Re-initialize integrations so they pick up new secrets
|
|
122
|
-
const integrations: string[] = [];
|
|
123
|
-
|
|
124
|
-
resetAgentMail();
|
|
125
|
-
if (initAgentMail()) integrations.push("agentmail");
|
|
126
|
-
|
|
127
|
-
resetGitHub();
|
|
128
|
-
if (initGitHub()) integrations.push("github");
|
|
129
|
-
|
|
130
|
-
resetLinear();
|
|
131
|
-
if (initLinear()) integrations.push("linear");
|
|
132
|
-
|
|
133
|
-
// Slack: stop and restart to pick up new token
|
|
134
|
-
await stopSlackApp();
|
|
135
|
-
await startSlackApp();
|
|
136
|
-
integrations.push("slack");
|
|
137
|
-
|
|
155
|
+
const result = await reloadGlobalConfigsAndIntegrations();
|
|
138
156
|
console.log(
|
|
139
|
-
`[reload-config] Loaded ${
|
|
157
|
+
`[reload-config] Loaded ${result.configsLoaded} config(s), re-initialized: ${result.integrationsReinitialized.join(", ") || "none"}`,
|
|
140
158
|
);
|
|
141
|
-
|
|
142
159
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
143
|
-
res.end(
|
|
144
|
-
JSON.stringify({
|
|
145
|
-
success: true,
|
|
146
|
-
configsLoaded: updated.length,
|
|
147
|
-
keysUpdated: updated,
|
|
148
|
-
integrationsReinitialized: integrations,
|
|
149
|
-
}),
|
|
150
|
-
);
|
|
160
|
+
res.end(JSON.stringify({ success: true, ...result }));
|
|
151
161
|
} catch (e) {
|
|
152
162
|
const message = e instanceof Error ? e.message : String(e);
|
|
153
163
|
console.error("[reload-config] Failed:", message);
|