whale-code 6.4.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/README.md +95 -0
- package/bin/swag-agent.js +9 -0
- package/bin/swagmanager-mcp.js +321 -0
- package/dist/cli/app.d.ts +26 -0
- package/dist/cli/app.js +64 -0
- package/dist/cli/chat/AgentSelector.d.ts +14 -0
- package/dist/cli/chat/AgentSelector.js +14 -0
- package/dist/cli/chat/ChatApp.d.ts +9 -0
- package/dist/cli/chat/ChatApp.js +267 -0
- package/dist/cli/chat/ChatInput.d.ts +39 -0
- package/dist/cli/chat/ChatInput.js +509 -0
- package/dist/cli/chat/MarkdownText.d.ts +10 -0
- package/dist/cli/chat/MarkdownText.js +20 -0
- package/dist/cli/chat/MessageList.d.ts +37 -0
- package/dist/cli/chat/MessageList.js +80 -0
- package/dist/cli/chat/ModelSelector.d.ts +20 -0
- package/dist/cli/chat/ModelSelector.js +73 -0
- package/dist/cli/chat/RewindViewer.d.ts +26 -0
- package/dist/cli/chat/RewindViewer.js +185 -0
- package/dist/cli/chat/StoreSelector.d.ts +14 -0
- package/dist/cli/chat/StoreSelector.js +24 -0
- package/dist/cli/chat/StreamingText.d.ts +12 -0
- package/dist/cli/chat/StreamingText.js +12 -0
- package/dist/cli/chat/SubagentPanel.d.ts +45 -0
- package/dist/cli/chat/SubagentPanel.js +110 -0
- package/dist/cli/chat/TeamPanel.d.ts +21 -0
- package/dist/cli/chat/TeamPanel.js +42 -0
- package/dist/cli/chat/ToolIndicator.d.ts +25 -0
- package/dist/cli/chat/ToolIndicator.js +436 -0
- package/dist/cli/chat/hooks/useAgentLoop.d.ts +39 -0
- package/dist/cli/chat/hooks/useAgentLoop.js +382 -0
- package/dist/cli/chat/hooks/useSlashCommands.d.ts +37 -0
- package/dist/cli/chat/hooks/useSlashCommands.js +387 -0
- package/dist/cli/commands/config-cmd.d.ts +10 -0
- package/dist/cli/commands/config-cmd.js +99 -0
- package/dist/cli/commands/doctor.d.ts +14 -0
- package/dist/cli/commands/doctor.js +172 -0
- package/dist/cli/commands/init.d.ts +16 -0
- package/dist/cli/commands/init.js +278 -0
- package/dist/cli/commands/mcp.d.ts +12 -0
- package/dist/cli/commands/mcp.js +162 -0
- package/dist/cli/login/LoginApp.d.ts +7 -0
- package/dist/cli/login/LoginApp.js +157 -0
- package/dist/cli/print-mode.d.ts +31 -0
- package/dist/cli/print-mode.js +202 -0
- package/dist/cli/serve-mode.d.ts +37 -0
- package/dist/cli/serve-mode.js +636 -0
- package/dist/cli/services/agent-definitions.d.ts +25 -0
- package/dist/cli/services/agent-definitions.js +91 -0
- package/dist/cli/services/agent-events.d.ts +178 -0
- package/dist/cli/services/agent-events.js +175 -0
- package/dist/cli/services/agent-loop.d.ts +90 -0
- package/dist/cli/services/agent-loop.js +762 -0
- package/dist/cli/services/agent-worker-base.d.ts +97 -0
- package/dist/cli/services/agent-worker-base.js +220 -0
- package/dist/cli/services/auth-service.d.ts +30 -0
- package/dist/cli/services/auth-service.js +160 -0
- package/dist/cli/services/background-processes.d.ts +126 -0
- package/dist/cli/services/background-processes.js +318 -0
- package/dist/cli/services/browser-auth.d.ts +24 -0
- package/dist/cli/services/browser-auth.js +180 -0
- package/dist/cli/services/claude-md-loader.d.ts +16 -0
- package/dist/cli/services/claude-md-loader.js +58 -0
- package/dist/cli/services/config-store.d.ts +47 -0
- package/dist/cli/services/config-store.js +79 -0
- package/dist/cli/services/debug-log.d.ts +10 -0
- package/dist/cli/services/debug-log.js +52 -0
- package/dist/cli/services/error-logger.d.ts +58 -0
- package/dist/cli/services/error-logger.js +269 -0
- package/dist/cli/services/file-history.d.ts +21 -0
- package/dist/cli/services/file-history.js +83 -0
- package/dist/cli/services/format-server-response.d.ts +16 -0
- package/dist/cli/services/format-server-response.js +440 -0
- package/dist/cli/services/git-context.d.ts +11 -0
- package/dist/cli/services/git-context.js +66 -0
- package/dist/cli/services/hooks.d.ts +85 -0
- package/dist/cli/services/hooks.js +258 -0
- package/dist/cli/services/interactive-tools.d.ts +125 -0
- package/dist/cli/services/interactive-tools.js +260 -0
- package/dist/cli/services/keybinding-manager.d.ts +52 -0
- package/dist/cli/services/keybinding-manager.js +115 -0
- package/dist/cli/services/local-tools.d.ts +22 -0
- package/dist/cli/services/local-tools.js +697 -0
- package/dist/cli/services/lsp-manager.d.ts +18 -0
- package/dist/cli/services/lsp-manager.js +717 -0
- package/dist/cli/services/mcp-client.d.ts +48 -0
- package/dist/cli/services/mcp-client.js +157 -0
- package/dist/cli/services/memory-manager.d.ts +16 -0
- package/dist/cli/services/memory-manager.js +57 -0
- package/dist/cli/services/model-manager.d.ts +18 -0
- package/dist/cli/services/model-manager.js +71 -0
- package/dist/cli/services/model-router.d.ts +26 -0
- package/dist/cli/services/model-router.js +149 -0
- package/dist/cli/services/permission-modes.d.ts +13 -0
- package/dist/cli/services/permission-modes.js +43 -0
- package/dist/cli/services/rewind.d.ts +84 -0
- package/dist/cli/services/rewind.js +194 -0
- package/dist/cli/services/ripgrep.d.ts +28 -0
- package/dist/cli/services/ripgrep.js +138 -0
- package/dist/cli/services/sandbox.d.ts +29 -0
- package/dist/cli/services/sandbox.js +97 -0
- package/dist/cli/services/server-tools.d.ts +61 -0
- package/dist/cli/services/server-tools.js +543 -0
- package/dist/cli/services/session-persistence.d.ts +23 -0
- package/dist/cli/services/session-persistence.js +99 -0
- package/dist/cli/services/subagent-worker.d.ts +19 -0
- package/dist/cli/services/subagent-worker.js +41 -0
- package/dist/cli/services/subagent.d.ts +47 -0
- package/dist/cli/services/subagent.js +647 -0
- package/dist/cli/services/system-prompt.d.ts +7 -0
- package/dist/cli/services/system-prompt.js +198 -0
- package/dist/cli/services/team-lead.d.ts +73 -0
- package/dist/cli/services/team-lead.js +512 -0
- package/dist/cli/services/team-state.d.ts +77 -0
- package/dist/cli/services/team-state.js +398 -0
- package/dist/cli/services/teammate.d.ts +31 -0
- package/dist/cli/services/teammate.js +689 -0
- package/dist/cli/services/telemetry.d.ts +61 -0
- package/dist/cli/services/telemetry.js +209 -0
- package/dist/cli/services/tools/agent-tools.d.ts +14 -0
- package/dist/cli/services/tools/agent-tools.js +347 -0
- package/dist/cli/services/tools/file-ops.d.ts +15 -0
- package/dist/cli/services/tools/file-ops.js +487 -0
- package/dist/cli/services/tools/search-tools.d.ts +8 -0
- package/dist/cli/services/tools/search-tools.js +186 -0
- package/dist/cli/services/tools/shell-exec.d.ts +10 -0
- package/dist/cli/services/tools/shell-exec.js +168 -0
- package/dist/cli/services/tools/task-manager.d.ts +28 -0
- package/dist/cli/services/tools/task-manager.js +209 -0
- package/dist/cli/services/tools/web-tools.d.ts +11 -0
- package/dist/cli/services/tools/web-tools.js +395 -0
- package/dist/cli/setup/SetupApp.d.ts +9 -0
- package/dist/cli/setup/SetupApp.js +191 -0
- package/dist/cli/shared/MatrixIntro.d.ts +4 -0
- package/dist/cli/shared/MatrixIntro.js +83 -0
- package/dist/cli/shared/Theme.d.ts +74 -0
- package/dist/cli/shared/Theme.js +127 -0
- package/dist/cli/shared/WhaleBanner.d.ts +10 -0
- package/dist/cli/shared/WhaleBanner.js +12 -0
- package/dist/cli/shared/markdown.d.ts +21 -0
- package/dist/cli/shared/markdown.js +756 -0
- package/dist/cli/status/StatusApp.d.ts +4 -0
- package/dist/cli/status/StatusApp.js +105 -0
- package/dist/cli/stores/StoreApp.d.ts +7 -0
- package/dist/cli/stores/StoreApp.js +81 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +538 -0
- package/dist/local-agent/connection.d.ts +48 -0
- package/dist/local-agent/connection.js +332 -0
- package/dist/local-agent/discovery.d.ts +18 -0
- package/dist/local-agent/discovery.js +146 -0
- package/dist/local-agent/executor.d.ts +34 -0
- package/dist/local-agent/executor.js +241 -0
- package/dist/local-agent/index.d.ts +14 -0
- package/dist/local-agent/index.js +198 -0
- package/dist/node/adapters/base.d.ts +35 -0
- package/dist/node/adapters/base.js +10 -0
- package/dist/node/adapters/discord.d.ts +29 -0
- package/dist/node/adapters/discord.js +299 -0
- package/dist/node/adapters/email.d.ts +23 -0
- package/dist/node/adapters/email.js +218 -0
- package/dist/node/adapters/imessage.d.ts +17 -0
- package/dist/node/adapters/imessage.js +118 -0
- package/dist/node/adapters/slack.d.ts +26 -0
- package/dist/node/adapters/slack.js +259 -0
- package/dist/node/adapters/sms.d.ts +23 -0
- package/dist/node/adapters/sms.js +161 -0
- package/dist/node/adapters/telegram.d.ts +17 -0
- package/dist/node/adapters/telegram.js +101 -0
- package/dist/node/adapters/webchat.d.ts +27 -0
- package/dist/node/adapters/webchat.js +160 -0
- package/dist/node/adapters/whatsapp.d.ts +28 -0
- package/dist/node/adapters/whatsapp.js +230 -0
- package/dist/node/cli.d.ts +2 -0
- package/dist/node/cli.js +325 -0
- package/dist/node/config.d.ts +17 -0
- package/dist/node/config.js +31 -0
- package/dist/node/runtime.d.ts +50 -0
- package/dist/node/runtime.js +351 -0
- package/dist/server/handlers/__test-utils__/mock-supabase.d.ts +11 -0
- package/dist/server/handlers/__test-utils__/mock-supabase.js +393 -0
- package/dist/server/handlers/analytics.d.ts +17 -0
- package/dist/server/handlers/analytics.js +266 -0
- package/dist/server/handlers/api-keys.d.ts +6 -0
- package/dist/server/handlers/api-keys.js +221 -0
- package/dist/server/handlers/billing.d.ts +33 -0
- package/dist/server/handlers/billing.js +272 -0
- package/dist/server/handlers/browser.d.ts +10 -0
- package/dist/server/handlers/browser.js +517 -0
- package/dist/server/handlers/catalog.d.ts +99 -0
- package/dist/server/handlers/catalog.js +976 -0
- package/dist/server/handlers/comms.d.ts +254 -0
- package/dist/server/handlers/comms.js +588 -0
- package/dist/server/handlers/creations.d.ts +6 -0
- package/dist/server/handlers/creations.js +479 -0
- package/dist/server/handlers/crm.d.ts +89 -0
- package/dist/server/handlers/crm.js +538 -0
- package/dist/server/handlers/discovery.d.ts +6 -0
- package/dist/server/handlers/discovery.js +288 -0
- package/dist/server/handlers/embeddings.d.ts +92 -0
- package/dist/server/handlers/embeddings.js +197 -0
- package/dist/server/handlers/enrichment.d.ts +8 -0
- package/dist/server/handlers/enrichment.js +768 -0
- package/dist/server/handlers/image-gen.d.ts +6 -0
- package/dist/server/handlers/image-gen.js +409 -0
- package/dist/server/handlers/inventory.d.ts +319 -0
- package/dist/server/handlers/inventory.js +447 -0
- package/dist/server/handlers/kali.d.ts +10 -0
- package/dist/server/handlers/kali.js +210 -0
- package/dist/server/handlers/llm-providers.d.ts +6 -0
- package/dist/server/handlers/llm-providers.js +673 -0
- package/dist/server/handlers/local-agent.d.ts +6 -0
- package/dist/server/handlers/local-agent.js +118 -0
- package/dist/server/handlers/meta-ads.d.ts +111 -0
- package/dist/server/handlers/meta-ads.js +2279 -0
- package/dist/server/handlers/nodes.d.ts +33 -0
- package/dist/server/handlers/nodes.js +699 -0
- package/dist/server/handlers/operations.d.ts +138 -0
- package/dist/server/handlers/operations.js +131 -0
- package/dist/server/handlers/platform.d.ts +23 -0
- package/dist/server/handlers/platform.js +227 -0
- package/dist/server/handlers/supply-chain.d.ts +19 -0
- package/dist/server/handlers/supply-chain.js +327 -0
- package/dist/server/handlers/transcription.d.ts +17 -0
- package/dist/server/handlers/transcription.js +121 -0
- package/dist/server/handlers/video-gen.d.ts +6 -0
- package/dist/server/handlers/video-gen.js +466 -0
- package/dist/server/handlers/voice.d.ts +8 -0
- package/dist/server/handlers/voice.js +1146 -0
- package/dist/server/handlers/workflow-steps.d.ts +86 -0
- package/dist/server/handlers/workflow-steps.js +2349 -0
- package/dist/server/handlers/workflows.d.ts +7 -0
- package/dist/server/handlers/workflows.js +989 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +2427 -0
- package/dist/server/lib/batch-client.d.ts +80 -0
- package/dist/server/lib/batch-client.js +467 -0
- package/dist/server/lib/code-worker-pool.d.ts +31 -0
- package/dist/server/lib/code-worker-pool.js +224 -0
- package/dist/server/lib/code-worker.d.ts +1 -0
- package/dist/server/lib/code-worker.js +188 -0
- package/dist/server/lib/compaction-service.d.ts +32 -0
- package/dist/server/lib/compaction-service.js +162 -0
- package/dist/server/lib/logger.d.ts +19 -0
- package/dist/server/lib/logger.js +46 -0
- package/dist/server/lib/otel.d.ts +38 -0
- package/dist/server/lib/otel.js +126 -0
- package/dist/server/lib/pg-rate-limiter.d.ts +21 -0
- package/dist/server/lib/pg-rate-limiter.js +86 -0
- package/dist/server/lib/prompt-sanitizer.d.ts +37 -0
- package/dist/server/lib/prompt-sanitizer.js +177 -0
- package/dist/server/lib/provider-capabilities.d.ts +85 -0
- package/dist/server/lib/provider-capabilities.js +190 -0
- package/dist/server/lib/provider-failover.d.ts +74 -0
- package/dist/server/lib/provider-failover.js +210 -0
- package/dist/server/lib/rate-limiter.d.ts +39 -0
- package/dist/server/lib/rate-limiter.js +147 -0
- package/dist/server/lib/server-agent-loop.d.ts +107 -0
- package/dist/server/lib/server-agent-loop.js +667 -0
- package/dist/server/lib/server-subagent.d.ts +78 -0
- package/dist/server/lib/server-subagent.js +203 -0
- package/dist/server/lib/session-checkpoint.d.ts +51 -0
- package/dist/server/lib/session-checkpoint.js +145 -0
- package/dist/server/lib/ssrf-guard.d.ts +13 -0
- package/dist/server/lib/ssrf-guard.js +240 -0
- package/dist/server/lib/supabase-client.d.ts +7 -0
- package/dist/server/lib/supabase-client.js +78 -0
- package/dist/server/lib/template-resolver.d.ts +31 -0
- package/dist/server/lib/template-resolver.js +215 -0
- package/dist/server/lib/utils.d.ts +16 -0
- package/dist/server/lib/utils.js +147 -0
- package/dist/server/local-agent-gateway.d.ts +82 -0
- package/dist/server/local-agent-gateway.js +426 -0
- package/dist/server/providers/anthropic.d.ts +20 -0
- package/dist/server/providers/anthropic.js +199 -0
- package/dist/server/providers/bedrock.d.ts +20 -0
- package/dist/server/providers/bedrock.js +194 -0
- package/dist/server/providers/gemini.d.ts +24 -0
- package/dist/server/providers/gemini.js +486 -0
- package/dist/server/providers/openai.d.ts +24 -0
- package/dist/server/providers/openai.js +522 -0
- package/dist/server/providers/registry.d.ts +32 -0
- package/dist/server/providers/registry.js +58 -0
- package/dist/server/providers/shared.d.ts +32 -0
- package/dist/server/providers/shared.js +124 -0
- package/dist/server/providers/types.d.ts +92 -0
- package/dist/server/providers/types.js +12 -0
- package/dist/server/proxy-handlers.d.ts +6 -0
- package/dist/server/proxy-handlers.js +89 -0
- package/dist/server/tool-router.d.ts +149 -0
- package/dist/server/tool-router.js +803 -0
- package/dist/server/validation.d.ts +24 -0
- package/dist/server/validation.js +301 -0
- package/dist/server/worker.d.ts +19 -0
- package/dist/server/worker.js +201 -0
- package/dist/setup.d.ts +8 -0
- package/dist/setup.js +181 -0
- package/dist/shared/agent-core.d.ts +157 -0
- package/dist/shared/agent-core.js +534 -0
- package/dist/shared/anthropic-types.d.ts +105 -0
- package/dist/shared/anthropic-types.js +7 -0
- package/dist/shared/api-client.d.ts +90 -0
- package/dist/shared/api-client.js +379 -0
- package/dist/shared/constants.d.ts +33 -0
- package/dist/shared/constants.js +80 -0
- package/dist/shared/sse-parser.d.ts +26 -0
- package/dist/shared/sse-parser.js +259 -0
- package/dist/shared/tool-dispatch.d.ts +52 -0
- package/dist/shared/tool-dispatch.js +191 -0
- package/dist/shared/types.d.ts +72 -0
- package/dist/shared/types.js +7 -0
- package/dist/updater.d.ts +25 -0
- package/dist/updater.js +140 -0
- package/dist/webchat/widget.d.ts +0 -0
- package/dist/webchat/widget.js +397 -0
- package/package.json +95 -0
- package/src/cli/services/builtin-skills/commit.md +19 -0
- package/src/cli/services/builtin-skills/review-pr.md +21 -0
- package/src/cli/services/builtin-skills/review.md +18 -0
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
// server/handlers/discovery.ts — mDNS/DNS-SD local network device discovery
|
|
2
|
+
// Scan for POS terminals, printers, and other services on the local network.
|
|
3
|
+
// Uses bonjour-service (Zeroconf/mDNS implementation in TypeScript).
|
|
4
|
+
import Bonjour from "bonjour-service";
|
|
5
|
+
// ============================================================================
|
|
6
|
+
// MODULE-LEVEL STATE
|
|
7
|
+
// ============================================================================
|
|
8
|
+
/** Single Bonjour instance shared across advertise operations */
|
|
9
|
+
let bonjourInstance = null;
|
|
10
|
+
/** Map of advertised service name -> Service instance for stop/list */
|
|
11
|
+
const advertisedServices = new Map();
|
|
12
|
+
function getBonjour() {
|
|
13
|
+
if (!bonjourInstance) {
|
|
14
|
+
bonjourInstance = new Bonjour();
|
|
15
|
+
}
|
|
16
|
+
return bonjourInstance;
|
|
17
|
+
}
|
|
18
|
+
/** Common service types to scan when doing a broad discovery */
|
|
19
|
+
const COMMON_SERVICE_TYPES = [
|
|
20
|
+
"http",
|
|
21
|
+
"https",
|
|
22
|
+
"printer",
|
|
23
|
+
"ipp",
|
|
24
|
+
"pos",
|
|
25
|
+
"airplay",
|
|
26
|
+
"raop",
|
|
27
|
+
"smb",
|
|
28
|
+
"ftp",
|
|
29
|
+
"ssh",
|
|
30
|
+
"mqtt",
|
|
31
|
+
];
|
|
32
|
+
/**
|
|
33
|
+
* Scan the local network for mDNS services.
|
|
34
|
+
* Creates a short-lived Bonjour instance per scan to avoid memory leaks.
|
|
35
|
+
*/
|
|
36
|
+
async function scanServices(serviceType, timeoutMs) {
|
|
37
|
+
return new Promise((resolve, reject) => {
|
|
38
|
+
let bonjour;
|
|
39
|
+
try {
|
|
40
|
+
bonjour = new Bonjour();
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
reject(new Error(`mDNS not available on this host. This typically happens in Docker/cloud environments without multicast support. (${err instanceof Error ? err.message : String(err)})`));
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const services = [];
|
|
47
|
+
const seen = new Set();
|
|
48
|
+
// Strip leading underscore and trailing protocol if user passed full form
|
|
49
|
+
// e.g. "_http._tcp" -> "http"
|
|
50
|
+
let type = serviceType;
|
|
51
|
+
if (type.startsWith("_"))
|
|
52
|
+
type = type.slice(1);
|
|
53
|
+
if (type.endsWith("._tcp"))
|
|
54
|
+
type = type.slice(0, -5);
|
|
55
|
+
if (type.endsWith("._udp"))
|
|
56
|
+
type = type.slice(0, -5);
|
|
57
|
+
let browser;
|
|
58
|
+
try {
|
|
59
|
+
browser = bonjour.find({ type });
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
bonjour.destroy();
|
|
63
|
+
reject(new Error(`Failed to start mDNS browser: ${err instanceof Error ? err.message : String(err)}`));
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
browser.on("up", (service) => {
|
|
67
|
+
const key = `${service.name}::${service.type}::${service.port}`;
|
|
68
|
+
if (!seen.has(key)) {
|
|
69
|
+
seen.add(key);
|
|
70
|
+
services.push({
|
|
71
|
+
name: service.name,
|
|
72
|
+
type: service.type,
|
|
73
|
+
host: service.host,
|
|
74
|
+
port: service.port,
|
|
75
|
+
addresses: service.addresses,
|
|
76
|
+
txt: service.txt,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
const timer = setTimeout(() => {
|
|
81
|
+
try {
|
|
82
|
+
browser.stop();
|
|
83
|
+
bonjour.destroy();
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
// Ignore cleanup errors
|
|
87
|
+
}
|
|
88
|
+
resolve(services);
|
|
89
|
+
}, timeoutMs);
|
|
90
|
+
// Safety: if the browser errors out, resolve with whatever we have
|
|
91
|
+
browser.on("error", () => {
|
|
92
|
+
clearTimeout(timer);
|
|
93
|
+
try {
|
|
94
|
+
browser.stop();
|
|
95
|
+
bonjour.destroy();
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
// Ignore cleanup errors
|
|
99
|
+
}
|
|
100
|
+
resolve(services);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Multi-type scan: run parallel scans for common service types.
|
|
106
|
+
*/
|
|
107
|
+
async function scanAllCommonTypes(timeoutMs) {
|
|
108
|
+
const results = await Promise.allSettled(COMMON_SERVICE_TYPES.map((type) => scanServices(type, timeoutMs)));
|
|
109
|
+
const allServices = [];
|
|
110
|
+
const seen = new Set();
|
|
111
|
+
for (const result of results) {
|
|
112
|
+
if (result.status === "fulfilled") {
|
|
113
|
+
for (const svc of result.value) {
|
|
114
|
+
const key = `${svc.name}::${svc.type}::${svc.port}`;
|
|
115
|
+
if (!seen.has(key)) {
|
|
116
|
+
seen.add(key);
|
|
117
|
+
allServices.push(svc);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return allServices;
|
|
123
|
+
}
|
|
124
|
+
// ============================================================================
|
|
125
|
+
// ADVERTISE
|
|
126
|
+
// ============================================================================
|
|
127
|
+
function advertiseService(name, type, port, txt) {
|
|
128
|
+
if (advertisedServices.has(name)) {
|
|
129
|
+
return {
|
|
130
|
+
success: false,
|
|
131
|
+
error: `Service "${name}" is already being advertised. Stop it first with stop_advertise.`,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
// Strip protocol suffix from type for bonjour-service
|
|
135
|
+
let cleanType = type;
|
|
136
|
+
if (cleanType.startsWith("_"))
|
|
137
|
+
cleanType = cleanType.slice(1);
|
|
138
|
+
if (cleanType.endsWith("._tcp"))
|
|
139
|
+
cleanType = cleanType.slice(0, -5);
|
|
140
|
+
if (cleanType.endsWith("._udp"))
|
|
141
|
+
cleanType = cleanType.slice(0, -5);
|
|
142
|
+
try {
|
|
143
|
+
const bonjour = getBonjour();
|
|
144
|
+
const service = bonjour.publish({
|
|
145
|
+
name,
|
|
146
|
+
type: cleanType,
|
|
147
|
+
port,
|
|
148
|
+
txt: txt,
|
|
149
|
+
});
|
|
150
|
+
advertisedServices.set(name, service);
|
|
151
|
+
return { success: true };
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
return {
|
|
155
|
+
success: false,
|
|
156
|
+
error: `Failed to advertise service: ${err instanceof Error ? err.message : String(err)}`,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
// ============================================================================
|
|
161
|
+
// STOP ADVERTISE
|
|
162
|
+
// ============================================================================
|
|
163
|
+
function stopAdvertise(name) {
|
|
164
|
+
const service = advertisedServices.get(name);
|
|
165
|
+
if (!service) {
|
|
166
|
+
return {
|
|
167
|
+
success: false,
|
|
168
|
+
error: `No advertised service found with name "${name}". Currently advertised: ${advertisedServices.size === 0 ? "none" : Array.from(advertisedServices.keys()).join(", ")}`,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
try {
|
|
172
|
+
if (service.stop) {
|
|
173
|
+
service.stop();
|
|
174
|
+
}
|
|
175
|
+
// The service also has a 'destroyed' flag — set externally isn't needed
|
|
176
|
+
// since stop() handles cleanup in bonjour-service
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
// Best-effort cleanup
|
|
180
|
+
}
|
|
181
|
+
advertisedServices.delete(name);
|
|
182
|
+
return { success: true };
|
|
183
|
+
}
|
|
184
|
+
// ============================================================================
|
|
185
|
+
// LIST ADVERTISED
|
|
186
|
+
// ============================================================================
|
|
187
|
+
function listAdvertised() {
|
|
188
|
+
const list = Array.from(advertisedServices.entries()).map(([name, svc]) => ({
|
|
189
|
+
name,
|
|
190
|
+
type: svc.type,
|
|
191
|
+
port: svc.port,
|
|
192
|
+
host: svc.host,
|
|
193
|
+
txt: svc.txt,
|
|
194
|
+
}));
|
|
195
|
+
return { success: true, data: list };
|
|
196
|
+
}
|
|
197
|
+
// ============================================================================
|
|
198
|
+
// MAIN HANDLER
|
|
199
|
+
// ============================================================================
|
|
200
|
+
export async function handleDiscovery(_sb, args, _storeId) {
|
|
201
|
+
const action = args.action;
|
|
202
|
+
switch (action) {
|
|
203
|
+
case "scan": {
|
|
204
|
+
const serviceType = args.service_type || "";
|
|
205
|
+
const timeoutMs = Math.min(Math.max(args.timeout_ms || 5000, 1000), 30000); // clamp 1-30s
|
|
206
|
+
try {
|
|
207
|
+
let services;
|
|
208
|
+
if (!serviceType || serviceType === "all") {
|
|
209
|
+
services = await scanAllCommonTypes(timeoutMs);
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
services = await scanServices(serviceType, timeoutMs);
|
|
213
|
+
}
|
|
214
|
+
return {
|
|
215
|
+
success: true,
|
|
216
|
+
data: {
|
|
217
|
+
service_type: serviceType || "all (common types)",
|
|
218
|
+
timeout_ms: timeoutMs,
|
|
219
|
+
count: services.length,
|
|
220
|
+
services,
|
|
221
|
+
},
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
catch (err) {
|
|
225
|
+
return {
|
|
226
|
+
success: false,
|
|
227
|
+
error: err instanceof Error
|
|
228
|
+
? err.message
|
|
229
|
+
: `Scan failed: ${String(err)}`,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
case "advertise": {
|
|
234
|
+
const name = args.name;
|
|
235
|
+
const type = args.type;
|
|
236
|
+
const port = args.port;
|
|
237
|
+
const txt = args.txt;
|
|
238
|
+
if (!name)
|
|
239
|
+
return { success: false, error: "name is required for advertise" };
|
|
240
|
+
if (!type)
|
|
241
|
+
return { success: false, error: "type is required for advertise (e.g. _http._tcp)" };
|
|
242
|
+
if (!port || typeof port !== "number")
|
|
243
|
+
return { success: false, error: "port is required for advertise (must be a number)" };
|
|
244
|
+
const result = advertiseService(name, type, port, txt);
|
|
245
|
+
if (result.success) {
|
|
246
|
+
return {
|
|
247
|
+
success: true,
|
|
248
|
+
data: {
|
|
249
|
+
message: `Service "${name}" is now being advertised as ${type} on port ${port}`,
|
|
250
|
+
name,
|
|
251
|
+
type,
|
|
252
|
+
port,
|
|
253
|
+
txt: txt || null,
|
|
254
|
+
},
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
return result;
|
|
258
|
+
}
|
|
259
|
+
case "stop_advertise": {
|
|
260
|
+
const name = args.name;
|
|
261
|
+
if (!name)
|
|
262
|
+
return { success: false, error: "name is required for stop_advertise" };
|
|
263
|
+
const result = stopAdvertise(name);
|
|
264
|
+
if (result.success) {
|
|
265
|
+
return {
|
|
266
|
+
success: true,
|
|
267
|
+
data: { message: `Stopped advertising service "${name}"` },
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
return result;
|
|
271
|
+
}
|
|
272
|
+
case "list_advertised": {
|
|
273
|
+
const result = listAdvertised();
|
|
274
|
+
return {
|
|
275
|
+
success: true,
|
|
276
|
+
data: {
|
|
277
|
+
count: result.data.length,
|
|
278
|
+
services: result.data,
|
|
279
|
+
},
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
default:
|
|
283
|
+
return {
|
|
284
|
+
success: false,
|
|
285
|
+
error: `Unknown discovery action: "${action}". Valid actions: scan, advertise, stop_advertise, list_advertised`,
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { SupabaseClient } from "@supabase/supabase-js";
|
|
2
|
+
export declare function handleEmbeddings(sb: SupabaseClient, args: Record<string, unknown>, storeId?: string): Promise<{
|
|
3
|
+
success: boolean;
|
|
4
|
+
error: string;
|
|
5
|
+
data?: undefined;
|
|
6
|
+
count?: undefined;
|
|
7
|
+
} | {
|
|
8
|
+
success: boolean;
|
|
9
|
+
data: {
|
|
10
|
+
id: any;
|
|
11
|
+
source_type: any;
|
|
12
|
+
source_id: any;
|
|
13
|
+
created_at: any;
|
|
14
|
+
};
|
|
15
|
+
error?: undefined;
|
|
16
|
+
count?: undefined;
|
|
17
|
+
} | {
|
|
18
|
+
success: boolean;
|
|
19
|
+
count: any;
|
|
20
|
+
data: any;
|
|
21
|
+
error?: undefined;
|
|
22
|
+
} | {
|
|
23
|
+
success: boolean;
|
|
24
|
+
data: {
|
|
25
|
+
indexed: number;
|
|
26
|
+
message: string;
|
|
27
|
+
total_products?: undefined;
|
|
28
|
+
deleted?: undefined;
|
|
29
|
+
source_type?: undefined;
|
|
30
|
+
source_id?: undefined;
|
|
31
|
+
total?: undefined;
|
|
32
|
+
by_source_type?: undefined;
|
|
33
|
+
};
|
|
34
|
+
error?: undefined;
|
|
35
|
+
count?: undefined;
|
|
36
|
+
} | {
|
|
37
|
+
success: boolean;
|
|
38
|
+
error: string;
|
|
39
|
+
data: {
|
|
40
|
+
indexed: number;
|
|
41
|
+
message?: undefined;
|
|
42
|
+
total_products?: undefined;
|
|
43
|
+
deleted?: undefined;
|
|
44
|
+
source_type?: undefined;
|
|
45
|
+
source_id?: undefined;
|
|
46
|
+
total?: undefined;
|
|
47
|
+
by_source_type?: undefined;
|
|
48
|
+
};
|
|
49
|
+
count?: undefined;
|
|
50
|
+
} | {
|
|
51
|
+
success: boolean;
|
|
52
|
+
data: {
|
|
53
|
+
indexed: number;
|
|
54
|
+
total_products: number;
|
|
55
|
+
message?: undefined;
|
|
56
|
+
deleted?: undefined;
|
|
57
|
+
source_type?: undefined;
|
|
58
|
+
source_id?: undefined;
|
|
59
|
+
total?: undefined;
|
|
60
|
+
by_source_type?: undefined;
|
|
61
|
+
};
|
|
62
|
+
error?: undefined;
|
|
63
|
+
count?: undefined;
|
|
64
|
+
} | {
|
|
65
|
+
success: boolean;
|
|
66
|
+
data: {
|
|
67
|
+
deleted: boolean;
|
|
68
|
+
source_type: string | undefined;
|
|
69
|
+
source_id: string | undefined;
|
|
70
|
+
indexed?: undefined;
|
|
71
|
+
message?: undefined;
|
|
72
|
+
total_products?: undefined;
|
|
73
|
+
total?: undefined;
|
|
74
|
+
by_source_type?: undefined;
|
|
75
|
+
};
|
|
76
|
+
error?: undefined;
|
|
77
|
+
count?: undefined;
|
|
78
|
+
} | {
|
|
79
|
+
success: boolean;
|
|
80
|
+
data: {
|
|
81
|
+
total: number;
|
|
82
|
+
by_source_type: Record<string, number>;
|
|
83
|
+
indexed?: undefined;
|
|
84
|
+
message?: undefined;
|
|
85
|
+
total_products?: undefined;
|
|
86
|
+
deleted?: undefined;
|
|
87
|
+
source_type?: undefined;
|
|
88
|
+
source_id?: undefined;
|
|
89
|
+
};
|
|
90
|
+
error?: undefined;
|
|
91
|
+
count?: undefined;
|
|
92
|
+
}>;
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import OpenAI from "openai";
|
|
2
|
+
// ============================================================================
|
|
3
|
+
// EMBEDDINGS — Semantic vector search via OpenAI + pgvector
|
|
4
|
+
// Actions: embed, search, index_products, delete, stats
|
|
5
|
+
// ============================================================================
|
|
6
|
+
const EMBEDDING_MODEL = "text-embedding-3-small";
|
|
7
|
+
const MAX_CHARS = 32_000; // ~8000 tokens
|
|
8
|
+
const BATCH_SIZE = 100; // OpenAI max per request
|
|
9
|
+
/** Truncate text to stay within token limits */
|
|
10
|
+
function truncateText(text) {
|
|
11
|
+
return text.length > MAX_CHARS ? text.slice(0, MAX_CHARS) : text;
|
|
12
|
+
}
|
|
13
|
+
/** Get an OpenAI client by decrypting the stored API key */
|
|
14
|
+
async function getOpenAIClient(sb, storeId) {
|
|
15
|
+
const { data: apiKey, error } = await sb.rpc("decrypt_secret", {
|
|
16
|
+
p_name: "OPENAI_API_KEY",
|
|
17
|
+
p_store_id: storeId,
|
|
18
|
+
});
|
|
19
|
+
if (error || !apiKey) {
|
|
20
|
+
throw new Error("OpenAI API key not found. Add OPENAI_API_KEY to your tool secrets.");
|
|
21
|
+
}
|
|
22
|
+
return new OpenAI({ apiKey });
|
|
23
|
+
}
|
|
24
|
+
/** Generate embeddings for one or more texts (batched) */
|
|
25
|
+
async function generateEmbeddings(openai, texts) {
|
|
26
|
+
const results = [];
|
|
27
|
+
for (let i = 0; i < texts.length; i += BATCH_SIZE) {
|
|
28
|
+
const batch = texts.slice(i, i + BATCH_SIZE).map(truncateText);
|
|
29
|
+
const response = await openai.embeddings.create({
|
|
30
|
+
model: EMBEDDING_MODEL,
|
|
31
|
+
input: batch,
|
|
32
|
+
});
|
|
33
|
+
for (const item of response.data) {
|
|
34
|
+
results.push(item.embedding);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return results;
|
|
38
|
+
}
|
|
39
|
+
/** Build a text representation of a product for embedding */
|
|
40
|
+
function productToText(product) {
|
|
41
|
+
const parts = [];
|
|
42
|
+
if (product.name)
|
|
43
|
+
parts.push(String(product.name));
|
|
44
|
+
if (product.short_description)
|
|
45
|
+
parts.push(String(product.short_description));
|
|
46
|
+
if (product.description)
|
|
47
|
+
parts.push(String(product.description));
|
|
48
|
+
if (product.field_values && typeof product.field_values === "object") {
|
|
49
|
+
const fv = product.field_values;
|
|
50
|
+
for (const [key, val] of Object.entries(fv)) {
|
|
51
|
+
if (val !== null && val !== undefined && val !== "") {
|
|
52
|
+
parts.push(`${key}: ${String(val)}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return parts.join(". ");
|
|
57
|
+
}
|
|
58
|
+
// ============================================================================
|
|
59
|
+
// MAIN HANDLER
|
|
60
|
+
// ============================================================================
|
|
61
|
+
export async function handleEmbeddings(sb, args, storeId) {
|
|
62
|
+
const sid = storeId;
|
|
63
|
+
const action = args.action;
|
|
64
|
+
switch (action) {
|
|
65
|
+
// ======================== EMBED ========================
|
|
66
|
+
case "embed": {
|
|
67
|
+
const content = args.content;
|
|
68
|
+
if (!content)
|
|
69
|
+
return { success: false, error: "content is required for embed action" };
|
|
70
|
+
const openai = await getOpenAIClient(sb, sid);
|
|
71
|
+
const [embedding] = await generateEmbeddings(openai, [content]);
|
|
72
|
+
const sourceType = args.source_type || "text";
|
|
73
|
+
const sourceId = args.source_id;
|
|
74
|
+
const metadata = args.metadata || {};
|
|
75
|
+
// Upsert: delete existing embedding for same source_id if provided
|
|
76
|
+
if (sourceId) {
|
|
77
|
+
await sb.from("embeddings")
|
|
78
|
+
.delete()
|
|
79
|
+
.eq("store_id", sid)
|
|
80
|
+
.eq("source_type", sourceType)
|
|
81
|
+
.eq("source_id", sourceId);
|
|
82
|
+
}
|
|
83
|
+
const { data, error } = await sb.from("embeddings").insert({
|
|
84
|
+
store_id: sid,
|
|
85
|
+
content: truncateText(content),
|
|
86
|
+
embedding: JSON.stringify(embedding),
|
|
87
|
+
metadata,
|
|
88
|
+
source_type: sourceType,
|
|
89
|
+
source_id: sourceId || null,
|
|
90
|
+
}).select("id, source_type, source_id, created_at").single();
|
|
91
|
+
if (error)
|
|
92
|
+
return { success: false, error: error.message };
|
|
93
|
+
return { success: true, data };
|
|
94
|
+
}
|
|
95
|
+
// ======================== SEARCH ========================
|
|
96
|
+
case "search": {
|
|
97
|
+
const query = args.query;
|
|
98
|
+
if (!query)
|
|
99
|
+
return { success: false, error: "query is required for search action" };
|
|
100
|
+
const matchCount = args.match_count || 10;
|
|
101
|
+
const sourceType = args.source_type || null;
|
|
102
|
+
const threshold = args.similarity_threshold || 0.5;
|
|
103
|
+
const openai = await getOpenAIClient(sb, sid);
|
|
104
|
+
const [queryEmbedding] = await generateEmbeddings(openai, [query]);
|
|
105
|
+
const { data, error } = await sb.rpc("search_embeddings", {
|
|
106
|
+
p_store_id: sid,
|
|
107
|
+
p_query_embedding: JSON.stringify(queryEmbedding),
|
|
108
|
+
p_match_count: matchCount,
|
|
109
|
+
p_source_type: sourceType,
|
|
110
|
+
p_similarity_threshold: threshold,
|
|
111
|
+
});
|
|
112
|
+
if (error)
|
|
113
|
+
return { success: false, error: error.message };
|
|
114
|
+
return { success: true, count: data?.length ?? 0, data };
|
|
115
|
+
}
|
|
116
|
+
// ======================== INDEX PRODUCTS ========================
|
|
117
|
+
case "index_products": {
|
|
118
|
+
const { data: products, error: fetchErr } = await sb
|
|
119
|
+
.from("products")
|
|
120
|
+
.select("id, name, description, short_description, field_values")
|
|
121
|
+
.eq("store_id", sid);
|
|
122
|
+
if (fetchErr)
|
|
123
|
+
return { success: false, error: fetchErr.message };
|
|
124
|
+
if (!products || products.length === 0) {
|
|
125
|
+
return { success: true, data: { indexed: 0, message: "No products found to index." } };
|
|
126
|
+
}
|
|
127
|
+
const openai = await getOpenAIClient(sb, sid);
|
|
128
|
+
const texts = products.map(productToText);
|
|
129
|
+
const embeddings = await generateEmbeddings(openai, texts);
|
|
130
|
+
// Delete all existing product embeddings for this store
|
|
131
|
+
await sb.from("embeddings")
|
|
132
|
+
.delete()
|
|
133
|
+
.eq("store_id", sid)
|
|
134
|
+
.eq("source_type", "product");
|
|
135
|
+
// Insert in batches
|
|
136
|
+
let inserted = 0;
|
|
137
|
+
const rows = products.map((p, i) => ({
|
|
138
|
+
store_id: sid,
|
|
139
|
+
content: truncateText(texts[i]),
|
|
140
|
+
embedding: JSON.stringify(embeddings[i]),
|
|
141
|
+
metadata: { name: p.name },
|
|
142
|
+
source_type: "product",
|
|
143
|
+
source_id: p.id,
|
|
144
|
+
}));
|
|
145
|
+
for (let i = 0; i < rows.length; i += 500) {
|
|
146
|
+
const batch = rows.slice(i, i + 500);
|
|
147
|
+
const { error: insErr } = await sb.from("embeddings").insert(batch);
|
|
148
|
+
if (insErr)
|
|
149
|
+
return { success: false, error: insErr.message, data: { indexed: inserted } };
|
|
150
|
+
inserted += batch.length;
|
|
151
|
+
}
|
|
152
|
+
return {
|
|
153
|
+
success: true,
|
|
154
|
+
data: { indexed: inserted, total_products: products.length },
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
// ======================== DELETE ========================
|
|
158
|
+
case "delete": {
|
|
159
|
+
const sourceType = args.source_type;
|
|
160
|
+
const sourceId = args.source_id;
|
|
161
|
+
if (!sourceType && !sourceId) {
|
|
162
|
+
return { success: false, error: "Provide source_type and/or source_id to delete embeddings." };
|
|
163
|
+
}
|
|
164
|
+
let q = sb.from("embeddings").delete().eq("store_id", sid);
|
|
165
|
+
if (sourceType)
|
|
166
|
+
q = q.eq("source_type", sourceType);
|
|
167
|
+
if (sourceId)
|
|
168
|
+
q = q.eq("source_id", sourceId);
|
|
169
|
+
const { error } = await q;
|
|
170
|
+
if (error)
|
|
171
|
+
return { success: false, error: error.message };
|
|
172
|
+
return { success: true, data: { deleted: true, source_type: sourceType, source_id: sourceId } };
|
|
173
|
+
}
|
|
174
|
+
// ======================== STATS ========================
|
|
175
|
+
case "stats": {
|
|
176
|
+
const { data, error } = await sb
|
|
177
|
+
.from("embeddings")
|
|
178
|
+
.select("source_type")
|
|
179
|
+
.eq("store_id", sid);
|
|
180
|
+
if (error)
|
|
181
|
+
return { success: false, error: error.message };
|
|
182
|
+
const counts = {};
|
|
183
|
+
let total = 0;
|
|
184
|
+
for (const row of data || []) {
|
|
185
|
+
const st = row.source_type || "unknown";
|
|
186
|
+
counts[st] = (counts[st] || 0) + 1;
|
|
187
|
+
total++;
|
|
188
|
+
}
|
|
189
|
+
return { success: true, data: { total, by_source_type: counts } };
|
|
190
|
+
}
|
|
191
|
+
default:
|
|
192
|
+
return {
|
|
193
|
+
success: false,
|
|
194
|
+
error: `Unknown embeddings action: ${action}. Valid actions: embed, search, index_products, delete, stats`,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { SupabaseClient } from "@supabase/supabase-js";
|
|
2
|
+
export declare function handleEnrichment(sb: SupabaseClient, args: Record<string, unknown>, storeId?: string): Promise<{
|
|
3
|
+
success: boolean;
|
|
4
|
+
data?: unknown;
|
|
5
|
+
error?: string;
|
|
6
|
+
count?: number;
|
|
7
|
+
note?: string;
|
|
8
|
+
}>;
|