@lobu/gateway 3.0.9 → 3.0.12
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/api/platform.d.ts.map +1 -1
- package/dist/api/platform.js +7 -26
- package/dist/api/platform.js.map +1 -1
- package/dist/auth/mcp/proxy.d.ts +14 -0
- package/dist/auth/mcp/proxy.d.ts.map +1 -1
- package/dist/auth/mcp/proxy.js +149 -13
- package/dist/auth/mcp/proxy.js.map +1 -1
- package/dist/cli/gateway.d.ts.map +1 -1
- package/dist/cli/gateway.js +29 -0
- package/dist/cli/gateway.js.map +1 -1
- package/dist/connections/chat-instance-manager.d.ts.map +1 -1
- package/dist/connections/chat-instance-manager.js +2 -1
- package/dist/connections/chat-instance-manager.js.map +1 -1
- package/dist/connections/interaction-bridge.d.ts +9 -2
- package/dist/connections/interaction-bridge.d.ts.map +1 -1
- package/dist/connections/interaction-bridge.js +121 -261
- package/dist/connections/interaction-bridge.js.map +1 -1
- package/dist/interactions.d.ts +9 -43
- package/dist/interactions.d.ts.map +1 -1
- package/dist/interactions.js +10 -52
- package/dist/interactions.js.map +1 -1
- package/dist/routes/public/agent.d.ts +4 -0
- package/dist/routes/public/agent.d.ts.map +1 -1
- package/dist/routes/public/agent.js +21 -0
- package/dist/routes/public/agent.js.map +1 -1
- package/dist/services/core-services.d.ts.map +1 -1
- package/dist/services/core-services.js +4 -0
- package/dist/services/core-services.js.map +1 -1
- package/package.json +9 -9
- package/src/__tests__/agent-config-routes.test.ts +0 -254
- package/src/__tests__/agent-history-routes.test.ts +0 -72
- package/src/__tests__/agent-routes.test.ts +0 -68
- package/src/__tests__/agent-schedules-routes.test.ts +0 -59
- package/src/__tests__/agent-settings-store.test.ts +0 -323
- package/src/__tests__/bedrock-model-catalog.test.ts +0 -40
- package/src/__tests__/bedrock-openai-service.test.ts +0 -157
- package/src/__tests__/bedrock-provider-module.test.ts +0 -56
- package/src/__tests__/chat-instance-manager-slack.test.ts +0 -204
- package/src/__tests__/chat-response-bridge.test.ts +0 -131
- package/src/__tests__/config-memory-plugins.test.ts +0 -92
- package/src/__tests__/config-request-store.test.ts +0 -127
- package/src/__tests__/connection-routes.test.ts +0 -144
- package/src/__tests__/core-services-store-selection.test.ts +0 -92
- package/src/__tests__/docker-deployment.test.ts +0 -1211
- package/src/__tests__/embedded-deployment.test.ts +0 -342
- package/src/__tests__/grant-store.test.ts +0 -148
- package/src/__tests__/http-proxy.test.ts +0 -281
- package/src/__tests__/instruction-service.test.ts +0 -37
- package/src/__tests__/link-buttons.test.ts +0 -112
- package/src/__tests__/lobu.test.ts +0 -32
- package/src/__tests__/mcp-config-service.test.ts +0 -347
- package/src/__tests__/mcp-proxy.test.ts +0 -694
- package/src/__tests__/message-handler-bridge.test.ts +0 -17
- package/src/__tests__/model-selection.test.ts +0 -172
- package/src/__tests__/oauth-templates.test.ts +0 -39
- package/src/__tests__/platform-adapter-slack-send.test.ts +0 -114
- package/src/__tests__/platform-helpers-model-resolution.test.ts +0 -253
- package/src/__tests__/provider-inheritance.test.ts +0 -212
- package/src/__tests__/routes/cli-auth.test.ts +0 -337
- package/src/__tests__/routes/interactions.test.ts +0 -121
- package/src/__tests__/secret-proxy.test.ts +0 -85
- package/src/__tests__/session-manager.test.ts +0 -572
- package/src/__tests__/setup.ts +0 -133
- package/src/__tests__/skill-and-mcp-registry.test.ts +0 -203
- package/src/__tests__/slack-routes.test.ts +0 -161
- package/src/__tests__/system-config-resolver.test.ts +0 -75
- package/src/__tests__/system-message-limiter.test.ts +0 -89
- package/src/__tests__/system-skills-service.test.ts +0 -362
- package/src/__tests__/transcription-service.test.ts +0 -222
- package/src/__tests__/utils/rate-limiter.test.ts +0 -102
- package/src/__tests__/worker-connection-manager.test.ts +0 -497
- package/src/__tests__/worker-job-router.test.ts +0 -722
- package/src/api/index.ts +0 -1
- package/src/api/platform.ts +0 -292
- package/src/api/response-renderer.ts +0 -157
- package/src/auth/agent-metadata-store.ts +0 -168
- package/src/auth/api-auth-middleware.ts +0 -69
- package/src/auth/api-key-provider-module.ts +0 -213
- package/src/auth/base-provider-module.ts +0 -201
- package/src/auth/bedrock/provider-module.ts +0 -110
- package/src/auth/chatgpt/chatgpt-oauth-module.ts +0 -185
- package/src/auth/chatgpt/device-code-client.ts +0 -218
- package/src/auth/chatgpt/index.ts +0 -1
- package/src/auth/claude/oauth-module.ts +0 -280
- package/src/auth/cli/token-service.ts +0 -249
- package/src/auth/external/client.ts +0 -560
- package/src/auth/external/device-code-client.ts +0 -235
- package/src/auth/mcp/config-service.ts +0 -420
- package/src/auth/mcp/proxy.ts +0 -1086
- package/src/auth/mcp/string-substitution.ts +0 -17
- package/src/auth/mcp/tool-cache.ts +0 -90
- package/src/auth/oauth/base-client.ts +0 -267
- package/src/auth/oauth/client.ts +0 -153
- package/src/auth/oauth/credentials.ts +0 -7
- package/src/auth/oauth/providers.ts +0 -69
- package/src/auth/oauth/state-store.ts +0 -150
- package/src/auth/oauth-templates.ts +0 -179
- package/src/auth/provider-catalog.ts +0 -220
- package/src/auth/provider-model-options.ts +0 -41
- package/src/auth/settings/agent-settings-store.ts +0 -565
- package/src/auth/settings/auth-profiles-manager.ts +0 -216
- package/src/auth/settings/index.ts +0 -12
- package/src/auth/settings/model-preference-store.ts +0 -52
- package/src/auth/settings/model-selection.ts +0 -135
- package/src/auth/settings/resolved-settings-view.ts +0 -298
- package/src/auth/settings/template-utils.ts +0 -44
- package/src/auth/settings/token-service.ts +0 -88
- package/src/auth/system-env-store.ts +0 -98
- package/src/auth/user-agents-store.ts +0 -68
- package/src/channels/binding-service.ts +0 -214
- package/src/channels/index.ts +0 -4
- package/src/cli/gateway.ts +0 -1312
- package/src/cli/index.ts +0 -74
- package/src/commands/built-in-commands.ts +0 -80
- package/src/commands/command-dispatcher.ts +0 -94
- package/src/commands/command-reply-adapters.ts +0 -27
- package/src/config/file-loader.ts +0 -618
- package/src/config/index.ts +0 -588
- package/src/config/network-allowlist.ts +0 -71
- package/src/connections/chat-instance-manager.ts +0 -1284
- package/src/connections/chat-response-bridge.ts +0 -618
- package/src/connections/index.ts +0 -7
- package/src/connections/interaction-bridge.ts +0 -831
- package/src/connections/message-handler-bridge.ts +0 -440
- package/src/connections/platform-auth-methods.ts +0 -15
- package/src/connections/types.ts +0 -84
- package/src/gateway/connection-manager.ts +0 -291
- package/src/gateway/index.ts +0 -698
- package/src/gateway/job-router.ts +0 -201
- package/src/gateway-main.ts +0 -200
- package/src/index.ts +0 -41
- package/src/infrastructure/queue/index.ts +0 -12
- package/src/infrastructure/queue/queue-producer.ts +0 -148
- package/src/infrastructure/queue/redis-queue.ts +0 -361
- package/src/infrastructure/queue/types.ts +0 -133
- package/src/infrastructure/redis/system-message-limiter.ts +0 -94
- package/src/interactions/config-request-store.ts +0 -198
- package/src/interactions.ts +0 -363
- package/src/lobu.ts +0 -311
- package/src/metrics/prometheus.ts +0 -159
- package/src/modules/module-system.ts +0 -179
- package/src/orchestration/base-deployment-manager.ts +0 -900
- package/src/orchestration/deployment-utils.ts +0 -98
- package/src/orchestration/impl/docker-deployment.ts +0 -620
- package/src/orchestration/impl/embedded-deployment.ts +0 -268
- package/src/orchestration/impl/index.ts +0 -8
- package/src/orchestration/impl/k8s/deployment.ts +0 -1061
- package/src/orchestration/impl/k8s/helpers.ts +0 -610
- package/src/orchestration/impl/k8s/index.ts +0 -1
- package/src/orchestration/index.ts +0 -333
- package/src/orchestration/message-consumer.ts +0 -584
- package/src/orchestration/scheduled-wakeup.ts +0 -704
- package/src/permissions/approval-policy.ts +0 -36
- package/src/permissions/grant-store.ts +0 -219
- package/src/platform/file-handler.ts +0 -66
- package/src/platform/link-buttons.ts +0 -57
- package/src/platform/renderer-utils.ts +0 -44
- package/src/platform/response-renderer.ts +0 -84
- package/src/platform/unified-thread-consumer.ts +0 -194
- package/src/platform.ts +0 -318
- package/src/proxy/http-proxy.ts +0 -752
- package/src/proxy/proxy-manager.ts +0 -81
- package/src/proxy/secret-proxy.ts +0 -402
- package/src/proxy/token-refresh-job.ts +0 -143
- package/src/routes/internal/audio.ts +0 -141
- package/src/routes/internal/device-auth.ts +0 -652
- package/src/routes/internal/files.ts +0 -226
- package/src/routes/internal/history.ts +0 -69
- package/src/routes/internal/images.ts +0 -127
- package/src/routes/internal/interactions.ts +0 -84
- package/src/routes/internal/middleware.ts +0 -23
- package/src/routes/internal/schedule.ts +0 -226
- package/src/routes/internal/types.ts +0 -22
- package/src/routes/openapi-auto.ts +0 -239
- package/src/routes/public/agent-access.ts +0 -23
- package/src/routes/public/agent-config.ts +0 -675
- package/src/routes/public/agent-history.ts +0 -422
- package/src/routes/public/agent-schedules.ts +0 -296
- package/src/routes/public/agent.ts +0 -1086
- package/src/routes/public/agents.ts +0 -373
- package/src/routes/public/channels.ts +0 -191
- package/src/routes/public/cli-auth.ts +0 -896
- package/src/routes/public/connections.ts +0 -574
- package/src/routes/public/landing.ts +0 -16
- package/src/routes/public/oauth.ts +0 -147
- package/src/routes/public/settings-auth.ts +0 -104
- package/src/routes/public/slack.ts +0 -173
- package/src/routes/shared/agent-ownership.ts +0 -101
- package/src/routes/shared/token-verifier.ts +0 -34
- package/src/services/bedrock-model-catalog.ts +0 -217
- package/src/services/bedrock-openai-service.ts +0 -658
- package/src/services/core-services.ts +0 -1072
- package/src/services/image-generation-service.ts +0 -257
- package/src/services/instruction-service.ts +0 -318
- package/src/services/mcp-registry.ts +0 -94
- package/src/services/platform-helpers.ts +0 -287
- package/src/services/session-manager.ts +0 -262
- package/src/services/settings-resolver.ts +0 -74
- package/src/services/system-config-resolver.ts +0 -89
- package/src/services/system-skills-service.ts +0 -229
- package/src/services/transcription-service.ts +0 -684
- package/src/session.ts +0 -110
- package/src/spaces/index.ts +0 -1
- package/src/spaces/space-resolver.ts +0 -17
- package/src/stores/in-memory-agent-store.ts +0 -403
- package/src/stores/redis-agent-store.ts +0 -279
- package/src/utils/public-url.ts +0 -44
- package/src/utils/rate-limiter.ts +0 -94
- package/tsconfig.json +0 -33
- package/tsconfig.tsbuildinfo +0 -1
|
@@ -1,204 +0,0 @@
|
|
|
1
|
-
import { describe, expect, mock, test } from "bun:test";
|
|
2
|
-
import { ChatInstanceManager } from "../connections/chat-instance-manager";
|
|
3
|
-
|
|
4
|
-
class PipelineRedisMock {
|
|
5
|
-
private readonly operations: Array<() => Promise<unknown>> = [];
|
|
6
|
-
|
|
7
|
-
constructor(private readonly redis: RedisMock) {}
|
|
8
|
-
|
|
9
|
-
set(key: string, value: string): this {
|
|
10
|
-
this.operations.push(() => this.redis.set(key, value));
|
|
11
|
-
return this;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
sadd(key: string, member: string): this {
|
|
15
|
-
this.operations.push(() => this.redis.sadd(key, member));
|
|
16
|
-
return this;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
async exec(): Promise<unknown[]> {
|
|
20
|
-
const results: unknown[] = [];
|
|
21
|
-
for (const operation of this.operations) {
|
|
22
|
-
results.push(await operation());
|
|
23
|
-
}
|
|
24
|
-
return results;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
class RedisMock {
|
|
29
|
-
private readonly strings = new Map<string, string>();
|
|
30
|
-
private readonly sets = new Map<string, Set<string>>();
|
|
31
|
-
|
|
32
|
-
async get(key: string): Promise<string | null> {
|
|
33
|
-
return this.strings.get(key) ?? null;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
async set(key: string, value: string): Promise<"OK"> {
|
|
37
|
-
this.strings.set(key, value);
|
|
38
|
-
return "OK";
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
async del(...keys: string[]): Promise<number> {
|
|
42
|
-
let removed = 0;
|
|
43
|
-
for (const key of keys) {
|
|
44
|
-
const existed = this.strings.delete(key) || this.sets.delete(key);
|
|
45
|
-
if (existed) {
|
|
46
|
-
removed++;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
return removed;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
async sadd(key: string, ...members: string[]): Promise<number> {
|
|
53
|
-
if (!this.sets.has(key)) {
|
|
54
|
-
this.sets.set(key, new Set());
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const set = this.sets.get(key)!;
|
|
58
|
-
let added = 0;
|
|
59
|
-
for (const member of members) {
|
|
60
|
-
if (!set.has(member)) {
|
|
61
|
-
set.add(member);
|
|
62
|
-
added++;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
return added;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
async srem(key: string, ...members: string[]): Promise<number> {
|
|
69
|
-
const set = this.sets.get(key);
|
|
70
|
-
if (!set) {
|
|
71
|
-
return 0;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
let removed = 0;
|
|
75
|
-
for (const member of members) {
|
|
76
|
-
if (set.delete(member)) {
|
|
77
|
-
removed++;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
return removed;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
async smembers(key: string): Promise<string[]> {
|
|
84
|
-
return Array.from(this.sets.get(key) || []);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
pipeline(): PipelineRedisMock {
|
|
88
|
-
return new PipelineRedisMock(this);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
describe("ChatInstanceManager Slack marketplace support", () => {
|
|
93
|
-
test("ensureSlackWorkspaceConnection is idempotent per team", async () => {
|
|
94
|
-
const manager = new ChatInstanceManager() as any;
|
|
95
|
-
const redis = new RedisMock();
|
|
96
|
-
const startInstance = mock(async (connection: any) => {
|
|
97
|
-
manager.instances.set(connection.id, {
|
|
98
|
-
connection,
|
|
99
|
-
chat: { webhooks: { slack: async () => new Response("ok") } },
|
|
100
|
-
cleanup: async () => undefined,
|
|
101
|
-
});
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
manager.redis = redis;
|
|
105
|
-
manager.startInstance = startInstance;
|
|
106
|
-
manager.resolveSlackAdapterConfig = mock(() => ({
|
|
107
|
-
platform: "slack",
|
|
108
|
-
signingSecret: "signing-secret",
|
|
109
|
-
clientId: "client-id",
|
|
110
|
-
clientSecret: "client-secret",
|
|
111
|
-
}));
|
|
112
|
-
|
|
113
|
-
const first = await manager.ensureSlackWorkspaceConnection("T123", {
|
|
114
|
-
botToken: "xoxb-first-token",
|
|
115
|
-
botUserId: "U123",
|
|
116
|
-
teamName: "Acme",
|
|
117
|
-
});
|
|
118
|
-
const second = await manager.ensureSlackWorkspaceConnection("T123", {
|
|
119
|
-
botToken: "xoxb-second-token",
|
|
120
|
-
botUserId: "U456",
|
|
121
|
-
teamName: "Acme Updated",
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
expect(second.id).toBe(first.id);
|
|
125
|
-
expect(startInstance).toHaveBeenCalledTimes(2);
|
|
126
|
-
|
|
127
|
-
const connections = await manager.listConnections({ platform: "slack" });
|
|
128
|
-
expect(connections).toHaveLength(1);
|
|
129
|
-
|
|
130
|
-
const stored = JSON.parse(
|
|
131
|
-
(await redis.get(`connection:${first.id}`)) || "{}"
|
|
132
|
-
);
|
|
133
|
-
const decryptedConfig = manager.decryptConfig(stored.config);
|
|
134
|
-
|
|
135
|
-
expect(stored.metadata).toEqual({
|
|
136
|
-
teamId: "T123",
|
|
137
|
-
teamName: "Acme Updated",
|
|
138
|
-
botUserId: "U456",
|
|
139
|
-
});
|
|
140
|
-
expect(decryptedConfig.botToken).toBe("xoxb-second-token");
|
|
141
|
-
expect(decryptedConfig.botUserId).toBe("U456");
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
test("handleSlackAppWebhook prefers an exact team match", async () => {
|
|
145
|
-
const manager = new ChatInstanceManager() as any;
|
|
146
|
-
|
|
147
|
-
manager.findSlackConnectionByTeamId = mock(async (teamId: string) =>
|
|
148
|
-
teamId === "T123" ? { id: "conn-team" } : null
|
|
149
|
-
);
|
|
150
|
-
manager.getDefaultSlackConnection = mock(async () => ({
|
|
151
|
-
id: "conn-default",
|
|
152
|
-
}));
|
|
153
|
-
manager.ensureConnectionRunning = mock(async () => true);
|
|
154
|
-
manager.handleWebhook = mock(
|
|
155
|
-
async (connectionId: string, request: Request) => {
|
|
156
|
-
const body = await request.text();
|
|
157
|
-
return new Response(`${connectionId}:${body}`);
|
|
158
|
-
}
|
|
159
|
-
);
|
|
160
|
-
|
|
161
|
-
const body = JSON.stringify({ team_id: "T123", type: "event_callback" });
|
|
162
|
-
const response = await manager.handleSlackAppWebhook(
|
|
163
|
-
new Request("https://gateway.example.com/slack/events", {
|
|
164
|
-
method: "POST",
|
|
165
|
-
headers: { "content-type": "application/json" },
|
|
166
|
-
body,
|
|
167
|
-
})
|
|
168
|
-
);
|
|
169
|
-
|
|
170
|
-
expect(response.status).toBe(200);
|
|
171
|
-
expect(await response.text()).toBe(`conn-team:${body}`);
|
|
172
|
-
expect(manager.handleWebhook).toHaveBeenCalledTimes(1);
|
|
173
|
-
expect(manager.handleWebhook.mock.calls[0]?.[0]).toBe("conn-team");
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
test("handleSlackAppWebhook falls back to the default Slack connection", async () => {
|
|
177
|
-
const manager = new ChatInstanceManager() as any;
|
|
178
|
-
|
|
179
|
-
manager.findSlackConnectionByTeamId = mock(async () => null);
|
|
180
|
-
manager.getDefaultSlackConnection = mock(async () => ({
|
|
181
|
-
id: "conn-default",
|
|
182
|
-
}));
|
|
183
|
-
manager.ensureConnectionRunning = mock(async () => true);
|
|
184
|
-
manager.handleWebhook = mock(
|
|
185
|
-
async (connectionId: string, request: Request) => {
|
|
186
|
-
const body = await request.text();
|
|
187
|
-
return new Response(`${connectionId}:${body}`);
|
|
188
|
-
}
|
|
189
|
-
);
|
|
190
|
-
|
|
191
|
-
const body = JSON.stringify({ type: "url_verification" });
|
|
192
|
-
const response = await manager.handleSlackAppWebhook(
|
|
193
|
-
new Request("https://gateway.example.com/slack/events", {
|
|
194
|
-
method: "POST",
|
|
195
|
-
headers: { "content-type": "application/json" },
|
|
196
|
-
body,
|
|
197
|
-
})
|
|
198
|
-
);
|
|
199
|
-
|
|
200
|
-
expect(response.status).toBe(200);
|
|
201
|
-
expect(await response.text()).toBe(`conn-default:${body}`);
|
|
202
|
-
expect(manager.handleWebhook.mock.calls[0]?.[0]).toBe("conn-default");
|
|
203
|
-
});
|
|
204
|
-
});
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { ChatResponseBridge } from "../connections/chat-response-bridge";
|
|
3
|
-
|
|
4
|
-
function createRedisMock() {
|
|
5
|
-
return {
|
|
6
|
-
pipeline() {
|
|
7
|
-
return {
|
|
8
|
-
rpush() {
|
|
9
|
-
return this;
|
|
10
|
-
},
|
|
11
|
-
ltrim() {
|
|
12
|
-
return this;
|
|
13
|
-
},
|
|
14
|
-
expire() {
|
|
15
|
-
return this;
|
|
16
|
-
},
|
|
17
|
-
exec: async () => [],
|
|
18
|
-
};
|
|
19
|
-
},
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
describe("ChatResponseBridge.handleEphemeral", () => {
|
|
24
|
-
test("renders settings links as native buttons for Chat SDK targets", async () => {
|
|
25
|
-
const posts: unknown[] = [];
|
|
26
|
-
const target = {
|
|
27
|
-
post: async (payload: unknown) => {
|
|
28
|
-
posts.push(payload);
|
|
29
|
-
return { id: "msg-1" };
|
|
30
|
-
},
|
|
31
|
-
};
|
|
32
|
-
const manager = {
|
|
33
|
-
getInstance: () => ({
|
|
34
|
-
connection: { platform: "telegram" },
|
|
35
|
-
chat: {
|
|
36
|
-
channel: () => target,
|
|
37
|
-
},
|
|
38
|
-
}),
|
|
39
|
-
getServices: () => ({
|
|
40
|
-
getQueue: () => ({
|
|
41
|
-
getRedisClient: () => createRedisMock(),
|
|
42
|
-
}),
|
|
43
|
-
}),
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
const bridge = new ChatResponseBridge(manager as any);
|
|
47
|
-
await bridge.handleEphemeral({
|
|
48
|
-
messageId: "m1",
|
|
49
|
-
channelId: "123",
|
|
50
|
-
conversationId: "123",
|
|
51
|
-
userId: "u1",
|
|
52
|
-
teamId: "telegram",
|
|
53
|
-
timestamp: Date.now(),
|
|
54
|
-
platform: "telegram",
|
|
55
|
-
platformMetadata: {
|
|
56
|
-
connectionId: "conn-1",
|
|
57
|
-
chatId: "123",
|
|
58
|
-
},
|
|
59
|
-
content:
|
|
60
|
-
"Setup required: add OpenAI in settings before this bot can respond.\n\n[Open Agent Settings](https://example.com/connect/claim?claim=abc123)",
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
expect(posts).toHaveLength(1);
|
|
64
|
-
expect(posts[0]).toBeObject();
|
|
65
|
-
expect(posts[0]).toHaveProperty("card");
|
|
66
|
-
expect(posts[0]).toHaveProperty("fallbackText");
|
|
67
|
-
expect((posts[0] as { fallbackText: string }).fallbackText).toContain(
|
|
68
|
-
"Open Agent Settings: https://example.com/connect/claim?claim=abc123"
|
|
69
|
-
);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
test("buffers telegram markdown until completion", async () => {
|
|
73
|
-
const posts: unknown[] = [];
|
|
74
|
-
const target = {
|
|
75
|
-
post: async (payload: unknown) => {
|
|
76
|
-
posts.push(payload);
|
|
77
|
-
return { id: "msg-1", threadId: "123" };
|
|
78
|
-
},
|
|
79
|
-
};
|
|
80
|
-
const manager = {
|
|
81
|
-
getInstance: () => ({
|
|
82
|
-
connection: { platform: "telegram" },
|
|
83
|
-
chat: {
|
|
84
|
-
channel: () => target,
|
|
85
|
-
},
|
|
86
|
-
}),
|
|
87
|
-
getServices: () => ({
|
|
88
|
-
getQueue: () => ({
|
|
89
|
-
getRedisClient: () => createRedisMock(),
|
|
90
|
-
}),
|
|
91
|
-
}),
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
const bridge = new ChatResponseBridge(manager as any);
|
|
95
|
-
const basePayload = {
|
|
96
|
-
messageId: "m1",
|
|
97
|
-
channelId: "123",
|
|
98
|
-
conversationId: "123",
|
|
99
|
-
userId: "u1",
|
|
100
|
-
teamId: "telegram",
|
|
101
|
-
timestamp: Date.now(),
|
|
102
|
-
platform: "telegram",
|
|
103
|
-
platformMetadata: {
|
|
104
|
-
connectionId: "conn-1",
|
|
105
|
-
chatId: "123",
|
|
106
|
-
},
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
await bridge.handleDelta(
|
|
110
|
-
{
|
|
111
|
-
...basePayload,
|
|
112
|
-
delta: "*Reason",
|
|
113
|
-
},
|
|
114
|
-
"session-1"
|
|
115
|
-
);
|
|
116
|
-
await bridge.handleDelta(
|
|
117
|
-
{
|
|
118
|
-
...basePayload,
|
|
119
|
-
delta: "ing:* hello",
|
|
120
|
-
},
|
|
121
|
-
"session-1"
|
|
122
|
-
);
|
|
123
|
-
|
|
124
|
-
expect(posts).toHaveLength(0);
|
|
125
|
-
|
|
126
|
-
await bridge.handleCompletion(basePayload, "session-1");
|
|
127
|
-
|
|
128
|
-
expect(posts).toHaveLength(1);
|
|
129
|
-
expect(posts[0]).toEqual({ markdown: "*Reasoning:* hello" });
|
|
130
|
-
});
|
|
131
|
-
});
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import { afterEach, describe, expect, test } from "bun:test";
|
|
2
|
-
import { buildMemoryPlugins } from "../config";
|
|
3
|
-
|
|
4
|
-
const originalMemoryUrl = process.env.MEMORY_URL;
|
|
5
|
-
const originalDispatcherServiceName = process.env.DISPATCHER_SERVICE_NAME;
|
|
6
|
-
const originalKubernetesNamespace = process.env.KUBERNETES_NAMESPACE;
|
|
7
|
-
|
|
8
|
-
afterEach(() => {
|
|
9
|
-
if (originalMemoryUrl === undefined) {
|
|
10
|
-
delete process.env.MEMORY_URL;
|
|
11
|
-
} else {
|
|
12
|
-
process.env.MEMORY_URL = originalMemoryUrl;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
if (originalDispatcherServiceName === undefined) {
|
|
16
|
-
delete process.env.DISPATCHER_SERVICE_NAME;
|
|
17
|
-
} else {
|
|
18
|
-
process.env.DISPATCHER_SERVICE_NAME = originalDispatcherServiceName;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
if (originalKubernetesNamespace === undefined) {
|
|
22
|
-
delete process.env.KUBERNETES_NAMESPACE;
|
|
23
|
-
} else {
|
|
24
|
-
process.env.KUBERNETES_NAMESPACE = originalKubernetesNamespace;
|
|
25
|
-
}
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
describe("buildMemoryPlugins", () => {
|
|
29
|
-
test("returns native memory when MEMORY_URL is unset and plugin exists", () => {
|
|
30
|
-
delete process.env.MEMORY_URL;
|
|
31
|
-
|
|
32
|
-
expect(buildMemoryPlugins({ hasNativeMemoryPlugin: true })).toEqual([
|
|
33
|
-
{
|
|
34
|
-
source: "@openclaw/native-memory",
|
|
35
|
-
slot: "memory",
|
|
36
|
-
enabled: true,
|
|
37
|
-
},
|
|
38
|
-
]);
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
test("returns no plugin when MEMORY_URL is unset and native memory is unavailable", () => {
|
|
42
|
-
delete process.env.MEMORY_URL;
|
|
43
|
-
|
|
44
|
-
expect(buildMemoryPlugins({ hasNativeMemoryPlugin: false })).toEqual([]);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
test("falls back to native memory when Owletto plugin is unavailable", () => {
|
|
48
|
-
process.env.MEMORY_URL = "https://memory.example.com";
|
|
49
|
-
|
|
50
|
-
expect(
|
|
51
|
-
buildMemoryPlugins({
|
|
52
|
-
hasOwlettoPlugin: false,
|
|
53
|
-
hasNativeMemoryPlugin: true,
|
|
54
|
-
})
|
|
55
|
-
).toEqual([
|
|
56
|
-
{
|
|
57
|
-
source: "@openclaw/native-memory",
|
|
58
|
-
slot: "memory",
|
|
59
|
-
enabled: true,
|
|
60
|
-
},
|
|
61
|
-
]);
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
test("returns no plugin when neither Owletto nor native memory plugin exists", () => {
|
|
65
|
-
process.env.MEMORY_URL = "https://memory.example.com";
|
|
66
|
-
|
|
67
|
-
expect(
|
|
68
|
-
buildMemoryPlugins({
|
|
69
|
-
hasOwlettoPlugin: false,
|
|
70
|
-
hasNativeMemoryPlugin: false,
|
|
71
|
-
})
|
|
72
|
-
).toEqual([]);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
test("uses Owletto plugin when installed and MEMORY_URL is set", () => {
|
|
76
|
-
process.env.MEMORY_URL = "https://memory.example.com";
|
|
77
|
-
process.env.DISPATCHER_SERVICE_NAME = "lobu-gateway";
|
|
78
|
-
process.env.KUBERNETES_NAMESPACE = "lobu";
|
|
79
|
-
|
|
80
|
-
expect(buildMemoryPlugins({ hasOwlettoPlugin: true })).toEqual([
|
|
81
|
-
{
|
|
82
|
-
source: "@lobu/owletto-openclaw",
|
|
83
|
-
slot: "memory",
|
|
84
|
-
enabled: true,
|
|
85
|
-
config: {
|
|
86
|
-
mcpUrl: "http://lobu-gateway.lobu.svc.cluster.local:8080/mcp/owletto",
|
|
87
|
-
gatewayAuthUrl: "http://lobu-gateway.lobu.svc.cluster.local:8080",
|
|
88
|
-
},
|
|
89
|
-
},
|
|
90
|
-
]);
|
|
91
|
-
});
|
|
92
|
-
});
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import { describe, expect, mock, test } from "bun:test";
|
|
2
|
-
import {
|
|
3
|
-
applyPendingConfigRequest,
|
|
4
|
-
buildConfigRequestText,
|
|
5
|
-
} from "../interactions/config-request-store";
|
|
6
|
-
|
|
7
|
-
describe("config-request-store", () => {
|
|
8
|
-
test("applies skills, mcps, nix packages, and grants", async () => {
|
|
9
|
-
const updateSettings = mock(() => Promise.resolve());
|
|
10
|
-
const grant = mock(() => Promise.resolve());
|
|
11
|
-
const agentSettingsStore = {
|
|
12
|
-
getSettings: mock(() =>
|
|
13
|
-
Promise.resolve({
|
|
14
|
-
nixConfig: { packages: ["git"] },
|
|
15
|
-
skillsConfig: {
|
|
16
|
-
skills: [
|
|
17
|
-
{
|
|
18
|
-
repo: "existing-skill",
|
|
19
|
-
name: "Existing Skill",
|
|
20
|
-
description: "Existing",
|
|
21
|
-
enabled: false,
|
|
22
|
-
},
|
|
23
|
-
],
|
|
24
|
-
},
|
|
25
|
-
mcpServers: {
|
|
26
|
-
existing: { enabled: true, url: "https://existing.example.com" },
|
|
27
|
-
},
|
|
28
|
-
})
|
|
29
|
-
),
|
|
30
|
-
updateSettings,
|
|
31
|
-
};
|
|
32
|
-
const grantStore = { grant };
|
|
33
|
-
|
|
34
|
-
await applyPendingConfigRequest(
|
|
35
|
-
agentSettingsStore as any,
|
|
36
|
-
grantStore as any,
|
|
37
|
-
{
|
|
38
|
-
agentId: "agent-1",
|
|
39
|
-
reason: "Install requested skill",
|
|
40
|
-
skills: [
|
|
41
|
-
{
|
|
42
|
-
repo: "existing-skill",
|
|
43
|
-
name: "Existing Skill",
|
|
44
|
-
description: "Updated",
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
repo: "new-skill",
|
|
48
|
-
name: "New Skill",
|
|
49
|
-
description: "New",
|
|
50
|
-
},
|
|
51
|
-
],
|
|
52
|
-
mcpServers: [
|
|
53
|
-
{
|
|
54
|
-
id: "new-mcp",
|
|
55
|
-
name: "New MCP",
|
|
56
|
-
url: "https://mcp.example.com",
|
|
57
|
-
type: "sse",
|
|
58
|
-
},
|
|
59
|
-
],
|
|
60
|
-
nixPackages: ["git", "ffmpeg"],
|
|
61
|
-
grants: ["api.example.com"],
|
|
62
|
-
}
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
expect(updateSettings).toHaveBeenCalledTimes(1);
|
|
66
|
-
const [, updates] = updateSettings.mock.calls[0]!;
|
|
67
|
-
expect(updates.skillsConfig.skills).toHaveLength(2);
|
|
68
|
-
expect(
|
|
69
|
-
updates.skillsConfig.skills.find(
|
|
70
|
-
(skill: any) => skill.repo === "existing-skill"
|
|
71
|
-
).enabled
|
|
72
|
-
).toBe(true);
|
|
73
|
-
expect(
|
|
74
|
-
updates.skillsConfig.skills.find(
|
|
75
|
-
(skill: any) => skill.repo === "new-skill"
|
|
76
|
-
).name
|
|
77
|
-
).toBe("New Skill");
|
|
78
|
-
expect(updates.mcpServers["new-mcp"]).toMatchObject({
|
|
79
|
-
enabled: true,
|
|
80
|
-
url: "https://mcp.example.com",
|
|
81
|
-
type: "sse",
|
|
82
|
-
});
|
|
83
|
-
expect(updates.nixConfig.packages).toEqual(["git", "ffmpeg"]);
|
|
84
|
-
expect(grant).toHaveBeenCalledWith("agent-1", "api.example.com", null);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
test("skips settings writes when a request only grants permissions", async () => {
|
|
88
|
-
const updateSettings = mock(() => Promise.resolve());
|
|
89
|
-
const grant = mock(() => Promise.resolve());
|
|
90
|
-
const agentSettingsStore = {
|
|
91
|
-
getSettings: mock(() => Promise.resolve({})),
|
|
92
|
-
updateSettings,
|
|
93
|
-
};
|
|
94
|
-
const grantStore = { grant };
|
|
95
|
-
|
|
96
|
-
await applyPendingConfigRequest(
|
|
97
|
-
agentSettingsStore as any,
|
|
98
|
-
grantStore as any,
|
|
99
|
-
{
|
|
100
|
-
agentId: "agent-1",
|
|
101
|
-
reason: "Allow API access",
|
|
102
|
-
grants: ["api.example.com"],
|
|
103
|
-
}
|
|
104
|
-
);
|
|
105
|
-
|
|
106
|
-
expect(updateSettings).not.toHaveBeenCalled();
|
|
107
|
-
expect(grant).toHaveBeenCalledWith("agent-1", "api.example.com", null);
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
test("builds readable config request text", () => {
|
|
111
|
-
const text = buildConfigRequestText({
|
|
112
|
-
agentId: "agent-1",
|
|
113
|
-
reason: "Install skill",
|
|
114
|
-
message: "Needed for triage",
|
|
115
|
-
skills: [{ repo: "ops-triage", name: "Ops Triage" }],
|
|
116
|
-
nixPackages: ["ffmpeg"],
|
|
117
|
-
grants: ["api.example.com"],
|
|
118
|
-
providers: ["openai"],
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
expect(text).toContain("Configuration Change Request");
|
|
122
|
-
expect(text).toContain("Ops Triage");
|
|
123
|
-
expect(text).toContain("ffmpeg");
|
|
124
|
-
expect(text).toContain("api.example.com");
|
|
125
|
-
expect(text).toContain("openai");
|
|
126
|
-
});
|
|
127
|
-
});
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
2
|
-
import { MockRedisClient } from "@lobu/core/testing";
|
|
3
|
-
import { AgentMetadataStore } from "../auth/agent-metadata-store";
|
|
4
|
-
import { UserAgentsStore } from "../auth/user-agents-store";
|
|
5
|
-
import { createConnectionCrudRoutes } from "../routes/public/connections";
|
|
6
|
-
import { setAuthProvider } from "../routes/public/settings-auth";
|
|
7
|
-
|
|
8
|
-
describe("connection routes", () => {
|
|
9
|
-
let redis: MockRedisClient;
|
|
10
|
-
let agentMetadataStore: AgentMetadataStore;
|
|
11
|
-
let userAgentsStore: UserAgentsStore;
|
|
12
|
-
|
|
13
|
-
beforeEach(async () => {
|
|
14
|
-
redis = new MockRedisClient();
|
|
15
|
-
agentMetadataStore = new AgentMetadataStore(redis as any);
|
|
16
|
-
userAgentsStore = new UserAgentsStore(redis as any);
|
|
17
|
-
|
|
18
|
-
await agentMetadataStore.createAgent(
|
|
19
|
-
"agent-1",
|
|
20
|
-
"Agent 1",
|
|
21
|
-
"telegram",
|
|
22
|
-
"u1"
|
|
23
|
-
);
|
|
24
|
-
await agentMetadataStore.createAgent(
|
|
25
|
-
"sandbox-1",
|
|
26
|
-
"Sandbox 1",
|
|
27
|
-
"telegram",
|
|
28
|
-
"u1",
|
|
29
|
-
{
|
|
30
|
-
parentConnectionId: "conn-1",
|
|
31
|
-
}
|
|
32
|
-
);
|
|
33
|
-
await userAgentsStore.addAgent("telegram", "u1", "agent-1");
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
afterEach(() => {
|
|
37
|
-
setAuthProvider(null);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
function buildApp() {
|
|
41
|
-
return createConnectionCrudRoutes(
|
|
42
|
-
{
|
|
43
|
-
async listConnections(filters?: any) {
|
|
44
|
-
const connection = {
|
|
45
|
-
id: "conn-1",
|
|
46
|
-
platform: "telegram",
|
|
47
|
-
templateAgentId: "agent-1",
|
|
48
|
-
config: { platform: "telegram" },
|
|
49
|
-
settings: {},
|
|
50
|
-
metadata: {},
|
|
51
|
-
status: "active",
|
|
52
|
-
createdAt: 1,
|
|
53
|
-
updatedAt: 1,
|
|
54
|
-
};
|
|
55
|
-
if (
|
|
56
|
-
filters?.templateAgentId &&
|
|
57
|
-
filters.templateAgentId !== "agent-1"
|
|
58
|
-
) {
|
|
59
|
-
return [];
|
|
60
|
-
}
|
|
61
|
-
return [connection];
|
|
62
|
-
},
|
|
63
|
-
async getConnection(id: string) {
|
|
64
|
-
if (id !== "conn-1") return null;
|
|
65
|
-
return {
|
|
66
|
-
id: "conn-1",
|
|
67
|
-
platform: "telegram",
|
|
68
|
-
templateAgentId: "agent-1",
|
|
69
|
-
config: { platform: "telegram" },
|
|
70
|
-
settings: {},
|
|
71
|
-
metadata: {},
|
|
72
|
-
status: "active",
|
|
73
|
-
createdAt: 1,
|
|
74
|
-
updatedAt: 1,
|
|
75
|
-
};
|
|
76
|
-
},
|
|
77
|
-
has() {
|
|
78
|
-
return true;
|
|
79
|
-
},
|
|
80
|
-
getServices() {
|
|
81
|
-
return {
|
|
82
|
-
getQueue() {
|
|
83
|
-
return {
|
|
84
|
-
getRedisClient() {
|
|
85
|
-
return redis;
|
|
86
|
-
},
|
|
87
|
-
};
|
|
88
|
-
},
|
|
89
|
-
};
|
|
90
|
-
},
|
|
91
|
-
} as any,
|
|
92
|
-
{
|
|
93
|
-
userAgentsStore,
|
|
94
|
-
agentMetadataStore: {
|
|
95
|
-
getMetadata: (agentId: string) =>
|
|
96
|
-
agentMetadataStore.getMetadata(agentId),
|
|
97
|
-
listSandboxes: (connectionId: string) =>
|
|
98
|
-
agentMetadataStore.listSandboxes(connectionId),
|
|
99
|
-
},
|
|
100
|
-
}
|
|
101
|
-
);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
test("forbids non-admin sessions from listing all connections", async () => {
|
|
105
|
-
setAuthProvider(() => ({
|
|
106
|
-
userId: "u1",
|
|
107
|
-
platform: "telegram",
|
|
108
|
-
exp: Date.now() + 60_000,
|
|
109
|
-
}));
|
|
110
|
-
|
|
111
|
-
const response = await buildApp().request("/api/v1/connections");
|
|
112
|
-
expect(response.status).toBe(403);
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
test("allows external owner sessions to list connections for their agent", async () => {
|
|
116
|
-
setAuthProvider(() => ({
|
|
117
|
-
userId: "u1",
|
|
118
|
-
oauthUserId: "u1",
|
|
119
|
-
platform: "external",
|
|
120
|
-
exp: Date.now() + 60_000,
|
|
121
|
-
}));
|
|
122
|
-
|
|
123
|
-
const response = await buildApp().request(
|
|
124
|
-
"/api/v1/connections?templateAgentId=agent-1"
|
|
125
|
-
);
|
|
126
|
-
expect(response.status).toBe(200);
|
|
127
|
-
const data = (await response.json()) as any;
|
|
128
|
-
expect(data.connections).toHaveLength(1);
|
|
129
|
-
expect(data.connections[0]?.id).toBe("conn-1");
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
test("forbids sandbox listing when session cannot access the connection template agent", async () => {
|
|
133
|
-
setAuthProvider(() => ({
|
|
134
|
-
userId: "u2",
|
|
135
|
-
platform: "telegram",
|
|
136
|
-
exp: Date.now() + 60_000,
|
|
137
|
-
}));
|
|
138
|
-
|
|
139
|
-
const response = await buildApp().request(
|
|
140
|
-
"/api/v1/connections/conn-1/sandboxes"
|
|
141
|
-
);
|
|
142
|
-
expect(response.status).toBe(403);
|
|
143
|
-
});
|
|
144
|
-
});
|