@geminilight/mindos 0.6.73 → 0.6.75
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/_standalone/.mindos-build-version +1 -1
- package/_standalone/.next/BUILD_ID +1 -1
- package/_standalone/.next/app-path-routes-manifest.json +30 -30
- package/_standalone/.next/build-manifest.json +3 -3
- package/_standalone/.next/cache/.previewinfo +1 -1
- package/_standalone/.next/cache/.rscinfo +1 -1
- package/_standalone/.next/cache/config.json +3 -3
- package/_standalone/.next/prerender-manifest.json +3 -3
- package/_standalone/.next/react-loadable-manifest.json +5 -5
- package/_standalone/.next/required-server-files.json +14 -1
- package/_standalone/.next/server/app/.well-known/agent-card.json/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/_global-error.html +2 -2
- package/_standalone/.next/server/app/_global-error.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/_standalone/.next/server/app/_not-found/page.js +1 -1
- package/_standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/agents/[agentKey]/page.js +1 -1
- package/_standalone/.next/server/app/agents/[agentKey]/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/agents/[agentKey]/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/agents/page.js +1 -1
- package/_standalone/.next/server/app/agents/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/agents/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/a2a/agents/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/a2a/delegations/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/a2a/discover/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/a2a/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/acp/config/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/acp/detect/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/acp/install/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/acp/registry/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/acp/session/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/agent-activity/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/agents/copy-skill/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/agents/copy-skill/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/agents/custom/detect/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/agents/custom/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/ask/route.js +1 -1
- package/_standalone/.next/server/app/api/ask/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/ask/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/ask-sessions/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/auth/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/backlinks/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/backlinks/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/bootstrap/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/bootstrap/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/changes/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/changes/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/channels/verify/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/channels/verify/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/connect/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/embedding/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/embedding/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/export/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/export/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/extract-pdf/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/extract-pdf/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/file/import/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/file/import/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/file/raw/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/file/raw/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/file/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/file/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/files/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/git/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/graph/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/graph/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/health/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/im/activity/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/im/config/route.js +1 -1
- package/_standalone/.next/server/app/api/im/config/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/im/status/route.js +1 -7
- package/_standalone/.next/server/app/api/im/status/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/im/status/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/im/test/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/im/test/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/im/webhook/feishu/route.js +1 -7
- package/_standalone/.next/server/app/api/im/webhook/feishu/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/im/webhook/feishu/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/im/webhook-status/route.js +1 -7
- package/_standalone/.next/server/app/api/im/webhook-status/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/im/webhook-status/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/inbox/clip/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/inbox/clip/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/inbox/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/inbox/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/init/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/init/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/lint/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/lint/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/agents/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/mcp/agents/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/direct-tools/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/install/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/install-skill/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/restart/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/status/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/tools/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/uninstall/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/monitoring/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/monitoring/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/recent-files/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/recent-files/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/restart/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/search/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/search/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/settings/list-models/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/settings/reset-token/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/settings/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/settings/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/settings/test-key/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/setup/check-path/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/setup/check-port/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/setup/generate-token/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/setup/ls/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/setup/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/space-overview/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/space-overview/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/sync/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/tree-version/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/tree-version/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/uninstall/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/update-check/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/update-status/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/workflows/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/workflows/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/changelog/page.js +1 -1
- package/_standalone/.next/server/app/changelog/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/changelog/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/changes/page.js +1 -1
- package/_standalone/.next/server/app/changes/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/changes/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/echo/[segment]/page.js +1 -1
- package/_standalone/.next/server/app/echo/[segment]/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/echo/[segment]/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/echo/page.js +1 -1
- package/_standalone/.next/server/app/echo/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/echo/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/explore/page.js +1 -1
- package/_standalone/.next/server/app/explore/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/explore/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/help/page.js +1 -1
- package/_standalone/.next/server/app/help/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/help/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/inbox/history/page.js +1 -1
- package/_standalone/.next/server/app/inbox/history/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/inbox/history/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/login/page.js +1 -1
- package/_standalone/.next/server/app/login/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/page.js +1 -1
- package/_standalone/.next/server/app/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/setup/page.js +2 -2
- package/_standalone/.next/server/app/setup/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/trash/page.js +3 -3
- package/_standalone/.next/server/app/trash/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/trash/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/view/[...path]/page.js +3 -3
- package/_standalone/.next/server/app/view/[...path]/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/view/[...path]/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app-paths-manifest.json +30 -30
- package/_standalone/.next/server/chunks/1076.js +1 -1
- package/_standalone/.next/server/chunks/{2449.js → 2424.js} +2 -2
- package/_standalone/.next/server/chunks/2792.js +1 -0
- package/_standalone/.next/server/chunks/2885.js +7 -0
- package/_standalone/.next/server/chunks/3800.js +1 -1
- package/_standalone/.next/server/chunks/5299.js +1 -1
- package/_standalone/.next/server/chunks/5464.js +1 -1
- package/_standalone/.next/server/chunks/6022.js +28 -28
- package/_standalone/.next/server/chunks/6539.js +1 -1
- package/_standalone/.next/server/chunks/8388.js +2 -2
- package/_standalone/.next/server/middleware-build-manifest.js +1 -1
- package/_standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/_standalone/.next/server/pages/500.html +2 -2
- package/_standalone/.next/server/server-reference-manifest.js +1 -1
- package/_standalone/.next/server/server-reference-manifest.json +1 -1
- package/_standalone/.next/static/chunks/{5331.c89084fd7f67887d.js → 4342.ccc18d7a45f204a4.js} +5 -5
- package/_standalone/.next/static/chunks/{4094.09364c01df411380.js → 4496.f77a677ac2034e48.js} +1 -1
- package/_standalone/.next/static/chunks/{5358-15618fc9b20018ee.js → 5358-15ceeb8c1cd9889d.js} +6 -6
- package/_standalone/.next/static/chunks/{6902-edc5c487c696bd0b.js → 6902-53a31f849968ddf8.js} +1 -1
- package/_standalone/.next/static/chunks/9207-456dc6941d557f33.js +1 -0
- package/_standalone/.next/static/chunks/app/{layout-fcbde5bee626d21a.js → layout-09f08f52e3cbfc47.js} +66 -66
- package/_standalone/.next/static/chunks/app/setup/{page-e40f3c5704c3c857.js → page-64d7fa097ef3f367.js} +1 -1
- package/_standalone/.next/static/chunks/app/trash/page-8e0ea3ba71702757.js +1 -0
- package/_standalone/.next/static/chunks/app/view/[...path]/page-04f43178679a8bfb.js +12 -0
- package/_standalone/.next/static/chunks/{webpack-dc486b68118d1328.js → webpack-ec9c6076012b75f3.js} +1 -1
- package/_standalone/.next/trace +72 -72
- package/_standalone/__tests__/agent/provider-presets.test.ts +32 -0
- package/_standalone/__tests__/api/im-config-feishu-conversation.test.ts +39 -0
- package/_standalone/__tests__/api/im-webhook-feishu.test.ts +21 -12
- package/_standalone/__tests__/api/pi-subagents.test.ts +81 -0
- package/_standalone/__tests__/ask/file-chip-variants.test.tsx +18 -0
- package/_standalone/__tests__/ask/message-list-agent-attribution.test.tsx +2 -0
- package/_standalone/__tests__/im/feishu-dispatcher.test.ts +151 -0
- package/_standalone/__tests__/im/feishu-webhook.test.ts +85 -57
- package/_standalone/__tests__/im/feishu-ws-client.test.ts +91 -0
- package/_standalone/__tests__/settings/custom-provider-form.test.ts +30 -0
- package/_standalone/components/ask/FileChip.tsx +1 -1
- package/_standalone/components/ask/MessageList.tsx +2 -2
- package/_standalone/components/settings/CustomProviderFields.tsx +1 -1
- package/_standalone/components/settings/CustomProvidersCard.tsx +3 -0
- package/_standalone/components/settings/useCustomProviderForm.ts +78 -24
- package/_standalone/components/shared/ProviderSelect.tsx +6 -5
- package/_standalone/next.config.ts +28 -4
- package/_standalone/package-lock.json +20 -2
- package/_standalone/package.json +3 -1
- package/_standalone/scripts/feishu-long-connection.ts +32 -0
- package/_standalone/server.js +1 -1
- package/_standalone/tsconfig.tsbuildinfo +1 -1
- package/app/__tests__/agent/provider-presets.test.ts +32 -0
- package/app/__tests__/api/im-config-feishu-conversation.test.ts +39 -0
- package/app/__tests__/api/im-webhook-feishu.test.ts +21 -12
- package/app/__tests__/api/pi-subagents.test.ts +81 -0
- package/app/__tests__/ask/file-chip-variants.test.tsx +18 -0
- package/app/__tests__/ask/message-list-agent-attribution.test.tsx +2 -0
- package/app/__tests__/im/feishu-dispatcher.test.ts +151 -0
- package/app/__tests__/im/feishu-webhook.test.ts +85 -57
- package/app/__tests__/im/feishu-ws-client.test.ts +91 -0
- package/app/__tests__/settings/custom-provider-form.test.ts +30 -0
- package/app/app/api/ask/route.ts +2 -0
- package/app/app/api/im/config/route.ts +2 -0
- package/app/app/api/im/webhook/feishu/route.ts +4 -2
- package/app/components/ask/FileChip.tsx +1 -1
- package/app/components/ask/MessageList.tsx +2 -2
- package/app/components/settings/CustomProviderFields.tsx +1 -1
- package/app/components/settings/CustomProvidersCard.tsx +3 -0
- package/app/components/settings/useCustomProviderForm.ts +78 -24
- package/app/components/shared/ProviderSelect.tsx +6 -5
- package/app/lib/agent/providers.ts +44 -6
- package/app/lib/im/feishu-dispatcher.ts +111 -0
- package/app/lib/im/feishu-ws-client.ts +72 -0
- package/app/lib/im/types.ts +14 -0
- package/app/lib/im/webhook/feishu.ts +44 -55
- package/app/next.config.ts +28 -4
- package/app/package.json +3 -1
- package/app/scripts/feishu-long-connection.ts +32 -0
- package/bin/cli.js +3 -1
- package/bin/commands/feishu-ws.js +39 -0
- package/package.json +1 -1
- package/scripts/build-runtime-archive.sh +36 -2
- package/_standalone/.next/static/chunks/9207-3b19c55a3c974a09.js +0 -1
- package/_standalone/.next/static/chunks/app/trash/page-e623ff0ab35de002.js +0 -1
- package/_standalone/.next/static/chunks/app/view/[...path]/page-49c4eff6ffdb5168.js +0 -12
- /package/_standalone/.next/static/{Dn8EHqUedSzanCfrM8WWS → dZDCx13MSM8QVQk2QNRs8}/_buildManifest.js +0 -0
- /package/_standalone/.next/static/{Dn8EHqUedSzanCfrM8WWS → dZDCx13MSM8QVQk2QNRs8}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { PROVIDER_PRESETS, getDefaultBaseUrl, groupedProviders } from '@/lib/agent/providers';
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
describe('provider presets', () => {
|
|
6
|
+
it('includes LM Studio as an explicit local preset', () => {
|
|
7
|
+
const preset = PROVIDER_PRESETS['lm-studio'];
|
|
8
|
+
expect(preset.name).toBe('LM Studio');
|
|
9
|
+
expect(preset.fixedBaseUrl).toBe('http://localhost:1234/v1');
|
|
10
|
+
expect(preset.apiKeyFallback).toBe('lm-studio');
|
|
11
|
+
expect(preset.supportsListModels).toBe(true);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('resolves LM Studio default base URL from its fixed preset URL', () => {
|
|
15
|
+
expect(getDefaultBaseUrl('lm-studio')).toBe('http://localhost:1234/v1');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('includes vLLM as an explicit local preset', () => {
|
|
19
|
+
const preset = PROVIDER_PRESETS.vllm;
|
|
20
|
+
expect(preset.name).toBe('vLLM');
|
|
21
|
+
expect(preset.fixedBaseUrl).toBe('http://localhost:8000/v1');
|
|
22
|
+
expect(preset.apiKeyFallback).toBe('vllm');
|
|
23
|
+
expect(preset.supportsListModels).toBe(true);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('groups Ollama, LM Studio, and vLLM under local providers', () => {
|
|
27
|
+
const groups = groupedProviders();
|
|
28
|
+
expect(groups.local).toContain('ollama');
|
|
29
|
+
expect(groups.local).toContain('lm-studio');
|
|
30
|
+
expect(groups.local).toContain('vllm');
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -100,4 +100,43 @@ describe('/api/im/config Feishu conversation', () => {
|
|
|
100
100
|
}),
|
|
101
101
|
}));
|
|
102
102
|
});
|
|
103
|
+
|
|
104
|
+
it('stores transport mode for Feishu conversation settings', async () => {
|
|
105
|
+
readIMConfig.mockReturnValue({
|
|
106
|
+
providers: {
|
|
107
|
+
feishu: {
|
|
108
|
+
app_id: 'cli_xxx',
|
|
109
|
+
app_secret: 'secret',
|
|
110
|
+
conversation: {
|
|
111
|
+
enabled: true,
|
|
112
|
+
transport: 'webhook',
|
|
113
|
+
allow_group_mentions: true,
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
const { PUT } = await importRoute();
|
|
119
|
+
const req = new NextRequest('http://localhost/api/im/config', {
|
|
120
|
+
method: 'PUT',
|
|
121
|
+
body: JSON.stringify({
|
|
122
|
+
platform: 'feishu',
|
|
123
|
+
conversation: {
|
|
124
|
+
enabled: true,
|
|
125
|
+
transport: 'long_connection',
|
|
126
|
+
},
|
|
127
|
+
}),
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
const res = await PUT(req);
|
|
131
|
+
expect(res.status).toBe(200);
|
|
132
|
+
expect(writeIMConfig).toHaveBeenCalledWith(expect.objectContaining({
|
|
133
|
+
providers: expect.objectContaining({
|
|
134
|
+
feishu: expect.objectContaining({
|
|
135
|
+
conversation: expect.objectContaining({
|
|
136
|
+
transport: 'long_connection',
|
|
137
|
+
}),
|
|
138
|
+
}),
|
|
139
|
+
}),
|
|
140
|
+
}));
|
|
141
|
+
});
|
|
103
142
|
});
|
|
@@ -2,14 +2,14 @@ import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
2
2
|
import { NextRequest } from 'next/server';
|
|
3
3
|
|
|
4
4
|
const getPlatformConfig = vi.fn();
|
|
5
|
-
const
|
|
5
|
+
const dispatchFeishuWebhook = vi.fn();
|
|
6
6
|
|
|
7
7
|
vi.mock('@/lib/im/config', () => ({
|
|
8
8
|
getPlatformConfig,
|
|
9
9
|
}));
|
|
10
10
|
|
|
11
|
-
vi.mock('@/lib/im/
|
|
12
|
-
|
|
11
|
+
vi.mock('@/lib/im/feishu-dispatcher', () => ({
|
|
12
|
+
dispatchFeishuWebhook,
|
|
13
13
|
}));
|
|
14
14
|
|
|
15
15
|
async function importRoute() {
|
|
@@ -36,15 +36,14 @@ describe('POST /api/im/webhook/feishu', () => {
|
|
|
36
36
|
expect(await res.json()).toEqual({ error: 'Feishu is not configured' });
|
|
37
37
|
});
|
|
38
38
|
|
|
39
|
-
it('
|
|
39
|
+
it('delegates webhook protocol handling to the SDK dispatcher wrapper', async () => {
|
|
40
40
|
getPlatformConfig.mockReturnValue({
|
|
41
41
|
app_id: 'cli_xxx',
|
|
42
42
|
app_secret: 'secret',
|
|
43
43
|
conversation: { enabled: true, encrypt_key: 'encrypt', public_base_url: 'https://mindos.example.com' },
|
|
44
44
|
});
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
kind: 'challenge',
|
|
46
|
+
dispatchFeishuWebhook.mockResolvedValue({
|
|
48
47
|
body: { challenge: 'challenge-token' },
|
|
49
48
|
status: 200,
|
|
50
49
|
});
|
|
@@ -53,24 +52,34 @@ describe('POST /api/im/webhook/feishu', () => {
|
|
|
53
52
|
const req = new NextRequest('http://localhost/api/im/webhook/feishu', {
|
|
54
53
|
method: 'POST',
|
|
55
54
|
body: JSON.stringify({ challenge: 'challenge-token' }),
|
|
56
|
-
headers: {
|
|
55
|
+
headers: {
|
|
56
|
+
'content-type': 'application/json',
|
|
57
|
+
'x-lark-request-timestamp': '1',
|
|
58
|
+
},
|
|
57
59
|
});
|
|
58
60
|
|
|
59
61
|
const res = await POST(req);
|
|
62
|
+
expect(dispatchFeishuWebhook).toHaveBeenCalledWith({
|
|
63
|
+
config: expect.objectContaining({ app_id: 'cli_xxx' }),
|
|
64
|
+
body: { challenge: 'challenge-token' },
|
|
65
|
+
headers: expect.objectContaining({
|
|
66
|
+
'content-type': 'application/json',
|
|
67
|
+
'x-lark-request-timestamp': '1',
|
|
68
|
+
}),
|
|
69
|
+
});
|
|
60
70
|
expect(res.status).toBe(200);
|
|
61
71
|
expect(await res.json()).toEqual({ challenge: 'challenge-token' });
|
|
62
72
|
});
|
|
63
73
|
|
|
64
|
-
it('returns accepted when
|
|
74
|
+
it('returns accepted when dispatcher queues an inbound event', async () => {
|
|
65
75
|
getPlatformConfig.mockReturnValue({
|
|
66
76
|
app_id: 'cli_xxx',
|
|
67
77
|
app_secret: 'secret',
|
|
68
78
|
conversation: { enabled: true, encrypt_key: 'encrypt', public_base_url: 'https://mindos.example.com' },
|
|
69
79
|
});
|
|
70
80
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
body: { ok: true },
|
|
81
|
+
dispatchFeishuWebhook.mockResolvedValue({
|
|
82
|
+
body: { ok: true, queued: true },
|
|
74
83
|
status: 202,
|
|
75
84
|
});
|
|
76
85
|
|
|
@@ -83,6 +92,6 @@ describe('POST /api/im/webhook/feishu', () => {
|
|
|
83
92
|
|
|
84
93
|
const res = await POST(req);
|
|
85
94
|
expect(res.status).toBe(202);
|
|
86
|
-
expect(await res.json()).toEqual({ ok: true });
|
|
95
|
+
expect(await res.json()).toEqual({ ok: true, queued: true });
|
|
87
96
|
});
|
|
88
97
|
});
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for built-in pi-subagents extension support.
|
|
3
|
+
*
|
|
4
|
+
* Verifies that MindOS correctly bundles and loads pi-subagents as a default
|
|
5
|
+
* extension, providing subagent tools (subagent, subagent_status) to the Agent.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, expect, it, beforeAll } from 'vitest';
|
|
9
|
+
import fs from 'fs';
|
|
10
|
+
import path from 'path';
|
|
11
|
+
|
|
12
|
+
const PROJECT_ROOT = path.resolve(__dirname, '..', '..');
|
|
13
|
+
|
|
14
|
+
describe('pi-subagents built-in extension', () => {
|
|
15
|
+
describe('dependency installation', () => {
|
|
16
|
+
it('pi-subagents is listed in package.json dependencies', () => {
|
|
17
|
+
const pkgPath = path.join(PROJECT_ROOT, 'package.json');
|
|
18
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
19
|
+
|
|
20
|
+
expect(pkg.dependencies).toHaveProperty('pi-subagents');
|
|
21
|
+
expect(pkg.dependencies['pi-subagents']).toMatch(/^\^?0\.\d+\.\d+$/);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('pi-subagents is installed in node_modules', () => {
|
|
25
|
+
const indexPath = path.join(PROJECT_ROOT, 'node_modules', 'pi-subagents', 'index.ts');
|
|
26
|
+
expect(fs.existsSync(indexPath)).toBe(true);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('pi-subagents has expected structure (agents directory)', () => {
|
|
30
|
+
const agentsDir = path.join(PROJECT_ROOT, 'node_modules', 'pi-subagents', 'agents');
|
|
31
|
+
expect(fs.existsSync(agentsDir)).toBe(true);
|
|
32
|
+
|
|
33
|
+
// Should have builtin agents like scout.md, planner.md
|
|
34
|
+
const agentFiles = fs.readdirSync(agentsDir);
|
|
35
|
+
expect(agentFiles.some((f) => f.endsWith('.md'))).toBe(true);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe('extension path registration', () => {
|
|
40
|
+
let askRouteContent: string;
|
|
41
|
+
|
|
42
|
+
beforeAll(() => {
|
|
43
|
+
const routePath = path.join(PROJECT_ROOT, 'app', 'api', 'ask', 'route.ts');
|
|
44
|
+
askRouteContent = fs.readFileSync(routePath, 'utf-8');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('ask/route.ts includes pi-subagents in additionalExtensionPaths', () => {
|
|
48
|
+
expect(askRouteContent).toContain('pi-subagents');
|
|
49
|
+
expect(askRouteContent).toContain("path.join(projectRoot, 'app', 'node_modules', 'pi-subagents', 'index.ts')");
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('pi-subagents path is after user extensions (scanExtensionPaths)', () => {
|
|
53
|
+
// User extensions should have priority, so scanExtensionPaths() comes first
|
|
54
|
+
const scanIndex = askRouteContent.indexOf('scanExtensionPaths()');
|
|
55
|
+
const subagentsIndex = askRouteContent.indexOf('pi-subagents');
|
|
56
|
+
|
|
57
|
+
expect(scanIndex).toBeGreaterThan(-1);
|
|
58
|
+
expect(subagentsIndex).toBeGreaterThan(scanIndex);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
describe('extension exports', () => {
|
|
63
|
+
it('pi-subagents index.ts is valid TypeScript with default export', async () => {
|
|
64
|
+
const indexPath = path.join(PROJECT_ROOT, 'node_modules', 'pi-subagents', 'index.ts');
|
|
65
|
+
const content = fs.readFileSync(indexPath, 'utf-8');
|
|
66
|
+
|
|
67
|
+
// Extension should have a default export function
|
|
68
|
+
expect(content).toMatch(/export\s+default\s+function/);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('pi-subagents registers subagent tool via pi.registerTool', async () => {
|
|
72
|
+
const indexPath = path.join(PROJECT_ROOT, 'node_modules', 'pi-subagents', 'index.ts');
|
|
73
|
+
const content = fs.readFileSync(indexPath, 'utf-8');
|
|
74
|
+
|
|
75
|
+
// Should call pi.registerTool with the subagent tool
|
|
76
|
+
expect(content).toContain('pi.registerTool');
|
|
77
|
+
// Should have tool definition for 'subagent'
|
|
78
|
+
expect(content).toMatch(/name:\s*['"]subagent['"]/);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
import { describe, it, expect } from 'vitest';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { renderToStaticMarkup } from 'react-dom/server';
|
|
5
|
+
import FileChip from '@/components/ask/FileChip';
|
|
6
|
+
|
|
7
|
+
describe('FileChip variants', () => {
|
|
8
|
+
it('renders ACP agent chips with an amber bot icon', () => {
|
|
9
|
+
const html = renderToStaticMarkup(
|
|
10
|
+
<FileChip path="Claude Code" variant="agent" onRemove={() => {}} />,
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
expect(html).toContain('Claude Code');
|
|
14
|
+
expect(html).toContain('lucide-bot');
|
|
15
|
+
expect(html).toContain('text-[var(--amber)]');
|
|
16
|
+
expect(html).not.toContain('lucide-zap');
|
|
17
|
+
});
|
|
18
|
+
});
|
|
@@ -46,6 +46,7 @@ describe('MessageList agent attribution', () => {
|
|
|
46
46
|
|
|
47
47
|
expect(html).toContain('Claude Code');
|
|
48
48
|
expect(html).toContain('Hello from Claude.');
|
|
49
|
+
expect(html).toContain('lucide-bot');
|
|
49
50
|
});
|
|
50
51
|
|
|
51
52
|
it('keeps the agent badge visible on assistant error bubbles', () => {
|
|
@@ -67,5 +68,6 @@ describe('MessageList agent attribution', () => {
|
|
|
67
68
|
|
|
68
69
|
expect(html).toContain('Claude Code');
|
|
69
70
|
expect(html).toContain('ACP Agent Error: timeout');
|
|
71
|
+
expect(html).toContain('lucide-bot');
|
|
70
72
|
});
|
|
71
73
|
});
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import type { FeishuConfig } from '@/lib/im/types';
|
|
3
|
+
|
|
4
|
+
const { dispatcherInvoke, dispatcherConstruct, generateChallenge } = vi.hoisted(() => {
|
|
5
|
+
const dispatcherInvoke = vi.fn();
|
|
6
|
+
const dispatcherConstruct = vi.fn();
|
|
7
|
+
const generateChallenge = vi.fn();
|
|
8
|
+
return { dispatcherInvoke, dispatcherConstruct, generateChallenge };
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
vi.mock('@larksuiteoapi/node-sdk', () => ({
|
|
12
|
+
EventDispatcher: class MockEventDispatcher {
|
|
13
|
+
constructor(params: unknown) {
|
|
14
|
+
dispatcherConstruct(params);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
register() {
|
|
18
|
+
return this;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async invoke(data: unknown) {
|
|
22
|
+
return await dispatcherInvoke(data);
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
generateChallenge,
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
describe('Feishu dispatcher', () => {
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
vi.clearAllMocks();
|
|
31
|
+
generateChallenge.mockReturnValue({ isChallenge: false, challenge: undefined });
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('returns ignored when webhook is not ready', async () => {
|
|
35
|
+
const { dispatchFeishuWebhook } = await import('@/lib/im/feishu-dispatcher');
|
|
36
|
+
const config: FeishuConfig = {
|
|
37
|
+
app_id: 'cli_xxx',
|
|
38
|
+
app_secret: 'secret',
|
|
39
|
+
conversation: {
|
|
40
|
+
enabled: true,
|
|
41
|
+
encrypt_key: 'encrypt',
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const result = await dispatchFeishuWebhook({
|
|
46
|
+
config,
|
|
47
|
+
body: { event: { message: { content: '{"text":"hi"}' } } },
|
|
48
|
+
headers: {},
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
expect(result).toEqual({
|
|
52
|
+
status: 202,
|
|
53
|
+
body: { ok: false, ignored: true, reason: 'Public base URL is required for Feishu event callbacks.' },
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('returns challenge response using SDK helper', async () => {
|
|
58
|
+
const { dispatchFeishuWebhook } = await import('@/lib/im/feishu-dispatcher');
|
|
59
|
+
generateChallenge.mockReturnValue({
|
|
60
|
+
isChallenge: true,
|
|
61
|
+
challenge: { challenge: 'challenge-token' },
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const result = await dispatchFeishuWebhook({
|
|
65
|
+
config: {
|
|
66
|
+
app_id: 'cli_xxx',
|
|
67
|
+
app_secret: 'secret',
|
|
68
|
+
conversation: {
|
|
69
|
+
enabled: true,
|
|
70
|
+
encrypt_key: 'encrypt',
|
|
71
|
+
public_base_url: 'https://mindos.example.com',
|
|
72
|
+
verification_token: 'verification-token',
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
body: { challenge: 'challenge-token' },
|
|
76
|
+
headers: { 'x-lark-request-timestamp': '1' },
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
expect(generateChallenge).toHaveBeenCalledWith(
|
|
80
|
+
expect.objectContaining({ challenge: 'challenge-token' }),
|
|
81
|
+
{ encryptKey: 'encrypt' },
|
|
82
|
+
);
|
|
83
|
+
expect(dispatcherInvoke).not.toHaveBeenCalled();
|
|
84
|
+
expect(result).toEqual({
|
|
85
|
+
status: 200,
|
|
86
|
+
body: { challenge: 'challenge-token' },
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('returns 401 when SDK verification fails', async () => {
|
|
91
|
+
const { dispatchFeishuWebhook } = await import('@/lib/im/feishu-dispatcher');
|
|
92
|
+
dispatcherInvoke.mockResolvedValue(undefined);
|
|
93
|
+
|
|
94
|
+
const result = await dispatchFeishuWebhook({
|
|
95
|
+
config: {
|
|
96
|
+
app_id: 'cli_xxx',
|
|
97
|
+
app_secret: 'secret',
|
|
98
|
+
conversation: {
|
|
99
|
+
enabled: true,
|
|
100
|
+
encrypt_key: 'encrypt',
|
|
101
|
+
public_base_url: 'https://mindos.example.com',
|
|
102
|
+
verification_token: 'verification-token',
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
body: { schema: '2.0', header: { event_type: 'im.message.receive_v1' } },
|
|
106
|
+
headers: {
|
|
107
|
+
'x-lark-request-timestamp': '1',
|
|
108
|
+
'x-lark-request-nonce': 'nonce',
|
|
109
|
+
'x-lark-signature': 'bad-signature',
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
expect(result).toEqual({
|
|
114
|
+
status: 401,
|
|
115
|
+
body: { ok: false, error: 'Invalid Feishu webhook signature or payload.' },
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('uses SDK dispatcher and returns the handler response for accepted events', async () => {
|
|
120
|
+
const { dispatchFeishuWebhook } = await import('@/lib/im/feishu-dispatcher');
|
|
121
|
+
dispatcherInvoke.mockResolvedValue({ ok: true, queued: true, reason: 'direct_message' });
|
|
122
|
+
|
|
123
|
+
const result = await dispatchFeishuWebhook({
|
|
124
|
+
config: {
|
|
125
|
+
app_id: 'cli_xxx',
|
|
126
|
+
app_secret: 'secret',
|
|
127
|
+
conversation: {
|
|
128
|
+
enabled: true,
|
|
129
|
+
encrypt_key: 'encrypt',
|
|
130
|
+
public_base_url: 'https://mindos.example.com',
|
|
131
|
+
verification_token: 'verification-token',
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
body: { schema: '2.0', header: { event_type: 'im.message.receive_v1' } },
|
|
135
|
+
headers: {
|
|
136
|
+
'x-lark-request-timestamp': '1',
|
|
137
|
+
'x-lark-request-nonce': 'nonce',
|
|
138
|
+
'x-lark-signature': 'signature',
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
expect(dispatcherInvoke).toHaveBeenCalledWith(expect.objectContaining({
|
|
143
|
+
schema: '2.0',
|
|
144
|
+
header: { event_type: 'im.message.receive_v1' },
|
|
145
|
+
}));
|
|
146
|
+
expect(result).toEqual({
|
|
147
|
+
status: 202,
|
|
148
|
+
body: { ok: true, queued: true, reason: 'direct_message' },
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
});
|
|
@@ -1,14 +1,35 @@
|
|
|
1
|
-
import { describe, it,
|
|
2
|
-
import type { FeishuConfig, FeishuWebhookEventEnvelope } from '@/lib/im/types';
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import type { FeishuConfig, FeishuSdkMessageEvent, FeishuWebhookEventEnvelope } from '@/lib/im/types';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
vi.mock('@/lib/im/executor', () => ({
|
|
5
|
+
sendIMMessage: vi.fn().mockResolvedValue({ ok: true, messageId: 'msg_1', timestamp: '2026-04-10T00:00:00.000Z' }),
|
|
6
|
+
}));
|
|
7
|
+
|
|
8
|
+
vi.mock('@/lib/agent/headless', () => ({
|
|
9
|
+
runHeadlessAgent: vi.fn().mockResolvedValue({ text: 'Hello from MindOS', thinking: '', toolCalls: [] }),
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
vi.mock('@/lib/im/conversation-store', () => ({
|
|
13
|
+
appendConversationTurn: vi.fn(),
|
|
14
|
+
getConversationHistory: vi.fn(() => ({ sessionId: 'session', messages: [] })),
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
vi.mock('@/lib/im/activity', () => ({
|
|
18
|
+
recordActivity: vi.fn(),
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
async function importModule() {
|
|
22
|
+
return await import('@/lib/im/webhook/feishu');
|
|
23
|
+
}
|
|
9
24
|
|
|
10
25
|
describe('Feishu webhook helpers', () => {
|
|
11
|
-
|
|
26
|
+
beforeEach(() => {
|
|
27
|
+
vi.clearAllMocks();
|
|
28
|
+
vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('builds pending status when conversation is enabled but public url is missing', async () => {
|
|
32
|
+
const { buildFeishuWebhookStatus } = await importModule();
|
|
12
33
|
const config: FeishuConfig = {
|
|
13
34
|
app_id: 'cli_xxx',
|
|
14
35
|
app_secret: 'secret',
|
|
@@ -27,7 +48,8 @@ describe('Feishu webhook helpers', () => {
|
|
|
27
48
|
});
|
|
28
49
|
});
|
|
29
50
|
|
|
30
|
-
it('builds ready status when conversation is enabled and callback URL is available', () => {
|
|
51
|
+
it('builds ready status when conversation is enabled and callback URL is available', async () => {
|
|
52
|
+
const { buildFeishuWebhookStatus } = await importModule();
|
|
31
53
|
const config: FeishuConfig = {
|
|
32
54
|
app_id: 'cli_xxx',
|
|
33
55
|
app_secret: 'secret',
|
|
@@ -47,27 +69,8 @@ describe('Feishu webhook helpers', () => {
|
|
|
47
69
|
});
|
|
48
70
|
});
|
|
49
71
|
|
|
50
|
-
it('
|
|
51
|
-
const {
|
|
52
|
-
const result = await handleFeishuWebhook({
|
|
53
|
-
config: {
|
|
54
|
-
app_id: 'cli_xxx',
|
|
55
|
-
app_secret: 'secret',
|
|
56
|
-
conversation: {
|
|
57
|
-
enabled: true,
|
|
58
|
-
encrypt_key: 'encrypt',
|
|
59
|
-
public_base_url: 'https://mindos.example.com',
|
|
60
|
-
verification_token: 'expected-token',
|
|
61
|
-
},
|
|
62
|
-
},
|
|
63
|
-
body: { challenge: 'challenge-token', token: 'wrong-token' },
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
expect(result.status).toBe(401);
|
|
67
|
-
expect(result.body).toEqual({ ok: false, error: 'Invalid verification token' });
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it('normalizes a dm text message into the shared incoming message shape', () => {
|
|
72
|
+
it('normalizes a dm text message into the shared incoming message shape', async () => {
|
|
73
|
+
const { normalizeFeishuIncomingMessage } = await importModule();
|
|
71
74
|
const payload: FeishuWebhookEventEnvelope = {
|
|
72
75
|
header: {
|
|
73
76
|
event_type: 'im.message.receive_v1',
|
|
@@ -99,7 +102,8 @@ describe('Feishu webhook helpers', () => {
|
|
|
99
102
|
});
|
|
100
103
|
});
|
|
101
104
|
|
|
102
|
-
it('processes direct messages even without mentions', () => {
|
|
105
|
+
it('processes direct messages even without mentions', async () => {
|
|
106
|
+
const { shouldProcessFeishuEvent } = await importModule();
|
|
103
107
|
const payload: FeishuWebhookEventEnvelope = {
|
|
104
108
|
event: {
|
|
105
109
|
message: {
|
|
@@ -112,7 +116,8 @@ describe('Feishu webhook helpers', () => {
|
|
|
112
116
|
expect(shouldProcessFeishuEvent(payload)).toEqual({ ok: true, reason: 'direct_message' });
|
|
113
117
|
});
|
|
114
118
|
|
|
115
|
-
it('ignores group messages without bot mentions', () => {
|
|
119
|
+
it('ignores group messages without bot mentions', async () => {
|
|
120
|
+
const { shouldProcessFeishuEvent } = await importModule();
|
|
116
121
|
const payload: FeishuWebhookEventEnvelope = {
|
|
117
122
|
event: {
|
|
118
123
|
message: {
|
|
@@ -126,34 +131,57 @@ describe('Feishu webhook helpers', () => {
|
|
|
126
131
|
expect(shouldProcessFeishuEvent(payload)).toEqual({ ok: false, reason: 'group_without_mention' });
|
|
127
132
|
});
|
|
128
133
|
|
|
129
|
-
it('
|
|
130
|
-
const {
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
conversation: {
|
|
136
|
-
enabled: true,
|
|
137
|
-
encrypt_key: 'encrypt',
|
|
138
|
-
public_base_url: 'https://mindos.example.com',
|
|
139
|
-
},
|
|
134
|
+
it('queues processing for direct message sdk events', async () => {
|
|
135
|
+
const { handleFeishuMessageReceiveEvent } = await importModule();
|
|
136
|
+
const payload: FeishuSdkMessageEvent = {
|
|
137
|
+
event_type: 'im.message.receive_v1',
|
|
138
|
+
sender: {
|
|
139
|
+
sender_id: { open_id: 'ou_sender_1' },
|
|
140
140
|
},
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
141
|
+
message: {
|
|
142
|
+
message_id: 'om_001',
|
|
143
|
+
chat_id: 'oc_chat_001',
|
|
144
|
+
chat_type: 'p2p',
|
|
145
|
+
content: JSON.stringify({ text: '你好' }),
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const result = await handleFeishuMessageReceiveEvent(payload);
|
|
150
|
+
|
|
151
|
+
expect(result).toEqual(expect.objectContaining({ ok: true, queued: true, reason: 'direct_message' }));
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('ignores sdk events with empty text after normalization', async () => {
|
|
155
|
+
const { handleFeishuMessageReceiveEvent } = await importModule();
|
|
156
|
+
const result = await handleFeishuMessageReceiveEvent({
|
|
157
|
+
event_type: 'im.message.receive_v1',
|
|
158
|
+
sender: {
|
|
159
|
+
sender_id: { open_id: 'ou_sender_1' },
|
|
160
|
+
},
|
|
161
|
+
message: {
|
|
162
|
+
chat_type: 'p2p',
|
|
163
|
+
chat_id: 'oc_chat_001',
|
|
164
|
+
message_id: 'om_001',
|
|
165
|
+
content: JSON.stringify({ text: ' ' }),
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
expect(result).toEqual({ ok: true, ignored: true, reason: 'empty_text' });
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it('ignores sdk events missing required message identifiers', async () => {
|
|
173
|
+
const { handleFeishuMessageReceiveEvent } = await importModule();
|
|
174
|
+
const result = await handleFeishuMessageReceiveEvent({
|
|
175
|
+
event_type: 'im.message.receive_v1',
|
|
176
|
+
sender: {
|
|
177
|
+
sender_id: { open_id: 'ou_sender_1' },
|
|
178
|
+
},
|
|
179
|
+
message: {
|
|
180
|
+
chat_type: 'p2p',
|
|
181
|
+
content: JSON.stringify({ text: 'hello' }),
|
|
153
182
|
},
|
|
154
183
|
});
|
|
155
184
|
|
|
156
|
-
expect(result
|
|
157
|
-
expect(result.body).toEqual({ ok: true, ignored: true, reason: 'empty_text' });
|
|
185
|
+
expect(result).toEqual({ ok: true, ignored: true, reason: 'invalid_event_payload' });
|
|
158
186
|
});
|
|
159
187
|
});
|