zyndo 0.1.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.
Files changed (47) hide show
  1. package/dist/agentLoop.d.ts +14 -0
  2. package/dist/agentLoop.js +76 -0
  3. package/dist/banner.d.ts +1 -0
  4. package/dist/banner.js +25 -0
  5. package/dist/config.d.ts +41 -0
  6. package/dist/config.js +109 -0
  7. package/dist/connection.d.ts +58 -0
  8. package/dist/connection.js +114 -0
  9. package/dist/index.d.ts +2 -0
  10. package/dist/index.js +68 -0
  11. package/dist/init.d.ts +1 -0
  12. package/dist/init.js +178 -0
  13. package/dist/mcp/mcpCore.d.ts +22 -0
  14. package/dist/mcp/mcpCore.js +448 -0
  15. package/dist/mcp/mcpServer.d.ts +1 -0
  16. package/dist/mcp/mcpServer.js +61 -0
  17. package/dist/providers/anthropic.d.ts +2 -0
  18. package/dist/providers/anthropic.js +52 -0
  19. package/dist/providers/claudeCode.d.ts +5 -0
  20. package/dist/providers/claudeCode.js +174 -0
  21. package/dist/providers/ollama.d.ts +2 -0
  22. package/dist/providers/ollama.js +90 -0
  23. package/dist/providers/openai.d.ts +2 -0
  24. package/dist/providers/openai.js +103 -0
  25. package/dist/providers/types.d.ts +33 -0
  26. package/dist/providers/types.js +4 -0
  27. package/dist/sellerDaemon.d.ts +9 -0
  28. package/dist/sellerDaemon.js +336 -0
  29. package/dist/state.d.ts +21 -0
  30. package/dist/state.js +63 -0
  31. package/dist/tools/askBuyer.d.ts +2 -0
  32. package/dist/tools/askBuyer.js +19 -0
  33. package/dist/tools/bash.d.ts +2 -0
  34. package/dist/tools/bash.js +35 -0
  35. package/dist/tools/glob.d.ts +2 -0
  36. package/dist/tools/glob.js +28 -0
  37. package/dist/tools/grep.d.ts +2 -0
  38. package/dist/tools/grep.js +36 -0
  39. package/dist/tools/pathSafety.d.ts +1 -0
  40. package/dist/tools/pathSafety.js +9 -0
  41. package/dist/tools/readFile.d.ts +2 -0
  42. package/dist/tools/readFile.js +35 -0
  43. package/dist/tools/types.d.ts +9 -0
  44. package/dist/tools/types.js +4 -0
  45. package/dist/tools/writeFile.d.ts +2 -0
  46. package/dist/tools/writeFile.js +28 -0
  47. package/package.json +36 -0
@@ -0,0 +1,61 @@
1
+ // ---------------------------------------------------------------------------
2
+ // MCP Server — stdio transport adapter
3
+ //
4
+ // Thin wrapper over mcpCore.ts. Reads JSON-RPC from stdin, writes to stdout.
5
+ // Manages a single McpSessionState and heartbeat timer for the stdio session.
6
+ // ---------------------------------------------------------------------------
7
+ import { createInterface } from 'node:readline';
8
+ import { heartbeat } from '../connection.js';
9
+ import { handleMcpMethod, mcpError } from './mcpCore.js';
10
+ // ---------------------------------------------------------------------------
11
+ // Heartbeat with auto-reconnect
12
+ // ---------------------------------------------------------------------------
13
+ function startHeartbeat(state) {
14
+ return setInterval(async () => {
15
+ if (state.agentSession === undefined)
16
+ return;
17
+ try {
18
+ await heartbeat(state.agentSession);
19
+ }
20
+ catch {
21
+ // Session expired — the next tool call will trigger auto-reconnect via mcpCore
22
+ }
23
+ }, 30_000);
24
+ }
25
+ // ---------------------------------------------------------------------------
26
+ // Start MCP server (stdio transport)
27
+ // ---------------------------------------------------------------------------
28
+ export async function startMcpServer() {
29
+ const state = {
30
+ agentSession: undefined,
31
+ lastConnectName: 'Claude Code Buyer',
32
+ lastEventId: 0,
33
+ bridgeUrl: process.env.ZYNDO_BRIDGE_URL ?? 'https://bridge.zyndo.ai',
34
+ apiKey: process.env.ZYNDO_API_KEY ?? ''
35
+ };
36
+ let heartbeatTimer;
37
+ const rl = createInterface({ input: process.stdin, terminal: false });
38
+ rl.on('line', async (line) => {
39
+ if (line.trim().length === 0)
40
+ return;
41
+ try {
42
+ const request = JSON.parse(line);
43
+ const response = await handleMcpMethod(state, request.method, request.id, request.params);
44
+ // Start heartbeat after a successful connect
45
+ if (request.method === 'tools/call' && state.agentSession !== undefined && heartbeatTimer === undefined) {
46
+ heartbeatTimer = startHeartbeat(state);
47
+ }
48
+ if (response.length > 0) {
49
+ process.stdout.write(response + '\n');
50
+ }
51
+ }
52
+ catch {
53
+ process.stdout.write(mcpError(null, -32700, 'Parse error') + '\n');
54
+ }
55
+ });
56
+ rl.on('close', () => {
57
+ if (heartbeatTimer !== undefined)
58
+ clearInterval(heartbeatTimer);
59
+ process.exit(0);
60
+ });
61
+ }
@@ -0,0 +1,2 @@
1
+ import type { LLMProvider } from './types.js';
2
+ export declare function createAnthropicProvider(apiKey: string, model: string): LLMProvider;
@@ -0,0 +1,52 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Anthropic Messages API adapter
3
+ // ---------------------------------------------------------------------------
4
+ export function createAnthropicProvider(apiKey, model) {
5
+ return {
6
+ async chat(messages, tools, systemPrompt) {
7
+ const anthropicMessages = messages.map((m) => ({
8
+ role: m.role,
9
+ content: m.content
10
+ }));
11
+ const anthropicTools = tools.map((t) => ({
12
+ name: t.name,
13
+ description: t.description,
14
+ input_schema: t.inputSchema
15
+ }));
16
+ const body = {
17
+ model,
18
+ max_tokens: 8192,
19
+ system: systemPrompt,
20
+ messages: anthropicMessages
21
+ };
22
+ if (anthropicTools.length > 0) {
23
+ body.tools = anthropicTools;
24
+ }
25
+ const res = await fetch('https://api.anthropic.com/v1/messages', {
26
+ method: 'POST',
27
+ headers: {
28
+ 'content-type': 'application/json',
29
+ 'x-api-key': apiKey,
30
+ 'anthropic-version': '2023-06-01'
31
+ },
32
+ body: JSON.stringify(body)
33
+ });
34
+ if (!res.ok) {
35
+ const text = await res.text();
36
+ throw new Error(`Anthropic API error (${res.status}): ${text}`);
37
+ }
38
+ const data = (await res.json());
39
+ return {
40
+ content: data.content,
41
+ stopReason: mapStopReason(data.stop_reason)
42
+ };
43
+ }
44
+ };
45
+ }
46
+ function mapStopReason(reason) {
47
+ if (reason === 'tool_use')
48
+ return 'tool_use';
49
+ if (reason === 'max_tokens')
50
+ return 'max_tokens';
51
+ return 'end_turn';
52
+ }
@@ -0,0 +1,5 @@
1
+ import type { SellerConfig, HarnessName } from '../config.js';
2
+ import type { AgentLoopResult } from '../agentLoop.js';
3
+ import type { DaemonLogger } from '../sellerDaemon.js';
4
+ export declare function detectHarness(binary: string): HarnessName;
5
+ export declare function runClaudeCodeTask(taskContext: string, config: SellerConfig, logger: DaemonLogger): Promise<AgentLoopResult>;
@@ -0,0 +1,174 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Claude Code provider — spawns a CLI harness (claude, codex, or generic)
3
+ // instead of making raw LLM API calls.
4
+ // ---------------------------------------------------------------------------
5
+ import { spawn } from 'node:child_process';
6
+ import { basename } from 'node:path';
7
+ // ---------------------------------------------------------------------------
8
+ // Harness detection and args
9
+ // ---------------------------------------------------------------------------
10
+ export function detectHarness(binary) {
11
+ const name = basename(binary).toLowerCase();
12
+ if (name === 'codex' || name.startsWith('codex-'))
13
+ return 'codex';
14
+ if (name === 'claude' || name.startsWith('claude-'))
15
+ return 'claude';
16
+ return 'generic';
17
+ }
18
+ function buildHarnessArgs(harness, config, systemPrompt) {
19
+ switch (harness) {
20
+ case 'claude': {
21
+ const args = [
22
+ '--print',
23
+ '--output-format', 'json',
24
+ '--system-prompt', systemPrompt,
25
+ '--model', config.model,
26
+ '--add-dir', config.workingDirectory,
27
+ '--permission-mode', 'bypassPermissions',
28
+ '--no-session-persistence'
29
+ ];
30
+ if (config.claudeCodeMaxBudgetUsd !== undefined) {
31
+ args.push('--max-budget-usd', String(config.claudeCodeMaxBudgetUsd));
32
+ }
33
+ return args;
34
+ }
35
+ case 'codex':
36
+ return [
37
+ '--quiet',
38
+ '--model', config.model,
39
+ '--approval-mode', 'full-auto'
40
+ ];
41
+ case 'generic':
42
+ return [];
43
+ }
44
+ }
45
+ // ---------------------------------------------------------------------------
46
+ // Prompt construction
47
+ // ---------------------------------------------------------------------------
48
+ function buildPrompt(taskContext, config) {
49
+ const skillLines = config.skills
50
+ .map((s) => `- ${s.id}: ${s.name} — ${s.description}`)
51
+ .join('\n');
52
+ return [
53
+ 'You are a seller agent on the Zyndo marketplace.',
54
+ '',
55
+ '**Your identity:**',
56
+ `- Name: ${config.name}`,
57
+ `- Description: ${config.description}`,
58
+ '',
59
+ '**Your skills:**',
60
+ skillLines,
61
+ '',
62
+ `**Working directory:** ${config.workingDirectory}`,
63
+ '',
64
+ '**Task from buyer:**',
65
+ taskContext,
66
+ '',
67
+ '**Instructions:**',
68
+ 'Do the work described above. Use the tools available to you (read files, write files, run commands, search code).',
69
+ '',
70
+ '**Delivery format — CRITICAL:**',
71
+ 'When finished, output the COMPLETE content of every file you created or modified.',
72
+ 'Each file MUST appear in a fenced code block with the filename as the info string:',
73
+ '',
74
+ '```filename.html',
75
+ '<full file content here>',
76
+ '```',
77
+ '',
78
+ 'Include ALL files. Do not summarize, abbreviate, or describe what you built.',
79
+ 'The text you output IS the deliverable — the buyer CANNOT access your filesystem.',
80
+ 'If multiple files, output each in its own code block.',
81
+ 'After all file blocks, add a 2-3 line summary of what was built.',
82
+ '',
83
+ 'If you need clarification from the buyer before you can proceed, output ONLY this JSON on a line by itself and stop:',
84
+ '{"__zyndo_ask_buyer__": "your question here"}',
85
+ 'Do not output anything else after this sentinel. Do not attempt to answer the question yourself.'
86
+ ].join('\n');
87
+ }
88
+ // ---------------------------------------------------------------------------
89
+ // Output parsing
90
+ // ---------------------------------------------------------------------------
91
+ const ASK_BUYER_RE = /\{"__zyndo_ask_buyer__":\s*"[^"]*"\}\s*$/;
92
+ function parseClaudeJsonOutput(raw) {
93
+ try {
94
+ const parsed = JSON.parse(raw);
95
+ return parsed.result ?? raw;
96
+ }
97
+ catch {
98
+ return raw;
99
+ }
100
+ }
101
+ function extractAskBuyerQuestion(text) {
102
+ const match = text.match(ASK_BUYER_RE);
103
+ if (match === null)
104
+ return undefined;
105
+ try {
106
+ const parsed = JSON.parse(match[0]);
107
+ return parsed.__zyndo_ask_buyer__;
108
+ }
109
+ catch {
110
+ return undefined;
111
+ }
112
+ }
113
+ function parseOutput(raw, harness) {
114
+ const text = harness === 'claude' ? parseClaudeJsonOutput(raw) : raw;
115
+ const trimmed = text.trim();
116
+ const question = extractAskBuyerQuestion(trimmed);
117
+ if (question !== undefined) {
118
+ return { output: '', paused: true, pendingQuestion: question };
119
+ }
120
+ return { output: trimmed, paused: false };
121
+ }
122
+ // ---------------------------------------------------------------------------
123
+ // Main entry point
124
+ // ---------------------------------------------------------------------------
125
+ const DEFAULT_TIMEOUT_MS = 600_000; // 10 minutes
126
+ export async function runClaudeCodeTask(taskContext, config, logger) {
127
+ const binary = config.claudeCodeBinary ?? 'claude';
128
+ const harness = config.harness ?? detectHarness(binary);
129
+ const timeoutMs = config.claudeCodeTimeoutMs ?? DEFAULT_TIMEOUT_MS;
130
+ const prompt = buildPrompt(taskContext, config);
131
+ const systemPrompt = config.systemPrompt;
132
+ const args = buildHarnessArgs(harness, config, systemPrompt);
133
+ logger.info(`Spawning ${harness} harness: ${binary} ${args.join(' ')}`);
134
+ return new Promise((resolve) => {
135
+ const controller = new AbortController();
136
+ const timeoutHandle = setTimeout(() => controller.abort(), timeoutMs);
137
+ const proc = spawn(binary, [...args], {
138
+ cwd: config.workingDirectory,
139
+ signal: controller.signal,
140
+ stdio: ['pipe', 'pipe', 'pipe'],
141
+ env: { ...process.env }
142
+ });
143
+ const stdoutChunks = [];
144
+ const stderrChunks = [];
145
+ proc.stdout.on('data', (chunk) => stdoutChunks.push(chunk));
146
+ proc.stderr.on('data', (chunk) => stderrChunks.push(chunk));
147
+ proc.on('error', (err) => {
148
+ clearTimeout(timeoutHandle);
149
+ if (err.name === 'AbortError') {
150
+ logger.error(`Task timed out after ${timeoutMs / 1000}s`);
151
+ resolve({ output: `Task timed out after ${timeoutMs / 1000} seconds.`, paused: false, timedOut: true });
152
+ return;
153
+ }
154
+ logger.error(`Spawn error: ${err.message}`);
155
+ resolve({ output: `Harness error: ${err.message}`, paused: false, timedOut: true });
156
+ });
157
+ proc.on('close', (code) => {
158
+ clearTimeout(timeoutHandle);
159
+ const stdout = Buffer.concat(stdoutChunks).toString('utf-8');
160
+ const stderr = Buffer.concat(stderrChunks).toString('utf-8');
161
+ if (stderr.length > 0) {
162
+ logger.info(`Harness stderr: ${stderr.slice(0, 500)}`);
163
+ }
164
+ if (code !== 0 && stdout.length === 0) {
165
+ logger.error(`Harness exited with code ${code}`);
166
+ resolve({ output: `Harness failed (exit ${code}): ${stderr.slice(0, 1000)}`, paused: false });
167
+ return;
168
+ }
169
+ resolve(parseOutput(stdout, harness));
170
+ });
171
+ proc.stdin.write(prompt);
172
+ proc.stdin.end();
173
+ });
174
+ }
@@ -0,0 +1,2 @@
1
+ import type { LLMProvider } from './types.js';
2
+ export declare function createOllamaProvider(model: string, baseUrl?: string): LLMProvider;
@@ -0,0 +1,90 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Ollama adapter (local models)
3
+ // ---------------------------------------------------------------------------
4
+ import { randomUUID } from 'node:crypto';
5
+ export function createOllamaProvider(model, baseUrl = 'http://localhost:11434') {
6
+ return {
7
+ async chat(messages, tools, systemPrompt) {
8
+ const ollamaMessages = [
9
+ { role: 'system', content: systemPrompt }
10
+ ];
11
+ for (const msg of messages) {
12
+ if (typeof msg.content === 'string') {
13
+ ollamaMessages.push({ role: msg.role, content: msg.content });
14
+ }
15
+ else {
16
+ const textParts = msg.content.filter((b) => b.type === 'text');
17
+ const toolUseParts = msg.content.filter((b) => b.type === 'tool_use');
18
+ if (msg.role === 'assistant') {
19
+ const text = textParts.map((t) => t.text).join('\n');
20
+ const toolCalls = toolUseParts.map((t) => ({
21
+ function: { name: t.name, arguments: t.input }
22
+ }));
23
+ ollamaMessages.push({
24
+ role: 'assistant',
25
+ content: text,
26
+ tool_calls: toolCalls.length > 0 ? toolCalls : undefined
27
+ });
28
+ }
29
+ else {
30
+ const toolResults = msg.content.filter((b) => b.type === 'tool_result');
31
+ for (const tr of toolResults) {
32
+ ollamaMessages.push({
33
+ role: 'tool',
34
+ content: 'content' in tr ? tr.content : ''
35
+ });
36
+ }
37
+ if (textParts.length > 0) {
38
+ ollamaMessages.push({ role: 'user', content: textParts.map((t) => t.text).join('\n') });
39
+ }
40
+ }
41
+ }
42
+ }
43
+ const ollamaTools = tools.map((t) => ({
44
+ type: 'function',
45
+ function: {
46
+ name: t.name,
47
+ description: t.description,
48
+ parameters: t.inputSchema
49
+ }
50
+ }));
51
+ const body = {
52
+ model,
53
+ messages: ollamaMessages,
54
+ stream: false
55
+ };
56
+ if (ollamaTools.length > 0) {
57
+ body.tools = ollamaTools;
58
+ }
59
+ const res = await fetch(`${baseUrl}/api/chat`, {
60
+ method: 'POST',
61
+ headers: { 'content-type': 'application/json' },
62
+ body: JSON.stringify(body)
63
+ });
64
+ if (!res.ok) {
65
+ const text = await res.text();
66
+ throw new Error(`Ollama API error (${res.status}): ${text}`);
67
+ }
68
+ const data = (await res.json());
69
+ const content = [];
70
+ if (data.message.content.length > 0) {
71
+ content.push({ type: 'text', text: data.message.content });
72
+ }
73
+ if (data.message.tool_calls !== undefined) {
74
+ for (const tc of data.message.tool_calls) {
75
+ content.push({
76
+ type: 'tool_use',
77
+ id: randomUUID(),
78
+ name: tc.function.name,
79
+ input: tc.function.arguments
80
+ });
81
+ }
82
+ }
83
+ const hasToolUse = content.some((b) => b.type === 'tool_use');
84
+ return {
85
+ content,
86
+ stopReason: hasToolUse ? 'tool_use' : 'end_turn'
87
+ };
88
+ }
89
+ };
90
+ }
@@ -0,0 +1,2 @@
1
+ import type { LLMProvider } from './types.js';
2
+ export declare function createOpenAIProvider(apiKey: string, model: string): LLMProvider;
@@ -0,0 +1,103 @@
1
+ // ---------------------------------------------------------------------------
2
+ // OpenAI Chat Completions adapter
3
+ // ---------------------------------------------------------------------------
4
+ export function createOpenAIProvider(apiKey, model) {
5
+ return {
6
+ async chat(messages, tools, systemPrompt) {
7
+ const openaiMessages = [
8
+ { role: 'system', content: systemPrompt }
9
+ ];
10
+ for (const msg of messages) {
11
+ if (typeof msg.content === 'string') {
12
+ openaiMessages.push({ role: msg.role, content: msg.content });
13
+ }
14
+ else {
15
+ // Map content blocks
16
+ const textParts = msg.content.filter((b) => b.type === 'text');
17
+ const toolUseParts = msg.content.filter((b) => b.type === 'tool_use');
18
+ const toolResultParts = msg.content.filter((b) => b.type === 'tool_result');
19
+ if (msg.role === 'assistant') {
20
+ const text = textParts.map((t) => t.text).join('\n') || null;
21
+ const toolCalls = toolUseParts.map((t) => ({
22
+ id: t.id,
23
+ type: 'function',
24
+ function: { name: t.name, arguments: JSON.stringify(t.input) }
25
+ }));
26
+ openaiMessages.push({
27
+ role: 'assistant',
28
+ content: text,
29
+ tool_calls: toolCalls.length > 0 ? toolCalls : undefined
30
+ });
31
+ }
32
+ else {
33
+ // User message with tool results
34
+ for (const tr of toolResultParts) {
35
+ if (tr.type === 'tool_result') {
36
+ openaiMessages.push({
37
+ role: 'tool',
38
+ content: 'content' in tr ? tr.content : '',
39
+ tool_call_id: 'tool_use_id' in tr ? tr.tool_use_id : ''
40
+ });
41
+ }
42
+ }
43
+ if (textParts.length > 0) {
44
+ openaiMessages.push({ role: 'user', content: textParts.map((t) => t.text).join('\n') });
45
+ }
46
+ }
47
+ }
48
+ }
49
+ const openaiTools = tools.map((t) => ({
50
+ type: 'function',
51
+ function: {
52
+ name: t.name,
53
+ description: t.description,
54
+ parameters: t.inputSchema
55
+ }
56
+ }));
57
+ const body = {
58
+ model,
59
+ messages: openaiMessages,
60
+ max_tokens: 8192
61
+ };
62
+ if (openaiTools.length > 0) {
63
+ body.tools = openaiTools;
64
+ }
65
+ const res = await fetch('https://api.openai.com/v1/chat/completions', {
66
+ method: 'POST',
67
+ headers: {
68
+ 'content-type': 'application/json',
69
+ 'authorization': `Bearer ${apiKey}`
70
+ },
71
+ body: JSON.stringify(body)
72
+ });
73
+ if (!res.ok) {
74
+ const text = await res.text();
75
+ throw new Error(`OpenAI API error (${res.status}): ${text}`);
76
+ }
77
+ const data = (await res.json());
78
+ const choice = data.choices[0];
79
+ const content = [];
80
+ if (choice.message.content !== null) {
81
+ content.push({ type: 'text', text: choice.message.content });
82
+ }
83
+ if (choice.message.tool_calls !== undefined) {
84
+ for (const tc of choice.message.tool_calls) {
85
+ content.push({
86
+ type: 'tool_use',
87
+ id: tc.id,
88
+ name: tc.function.name,
89
+ input: JSON.parse(tc.function.arguments)
90
+ });
91
+ }
92
+ }
93
+ return {
94
+ content,
95
+ stopReason: choice.finish_reason === 'tool_calls'
96
+ ? 'tool_use'
97
+ : choice.finish_reason === 'length'
98
+ ? 'max_tokens'
99
+ : 'end_turn'
100
+ };
101
+ }
102
+ };
103
+ }
@@ -0,0 +1,33 @@
1
+ export type ToolDefinition = Readonly<{
2
+ name: string;
3
+ description: string;
4
+ inputSchema: Record<string, unknown>;
5
+ }>;
6
+ export type TextBlock = Readonly<{
7
+ type: 'text';
8
+ text: string;
9
+ }>;
10
+ export type ToolUseBlock = Readonly<{
11
+ type: 'tool_use';
12
+ id: string;
13
+ name: string;
14
+ input: Record<string, unknown>;
15
+ }>;
16
+ export type ToolResultBlock = Readonly<{
17
+ type: 'tool_result';
18
+ tool_use_id: string;
19
+ content: string;
20
+ is_error?: boolean;
21
+ }>;
22
+ export type ContentBlock = TextBlock | ToolUseBlock;
23
+ export type Message = Readonly<{
24
+ role: 'user' | 'assistant';
25
+ content: string | ReadonlyArray<ContentBlock | ToolResultBlock>;
26
+ }>;
27
+ export type ChatResponse = Readonly<{
28
+ content: ReadonlyArray<ContentBlock>;
29
+ stopReason: 'end_turn' | 'tool_use' | 'max_tokens';
30
+ }>;
31
+ export type LLMProvider = Readonly<{
32
+ chat: (messages: ReadonlyArray<Message>, tools: ReadonlyArray<ToolDefinition>, systemPrompt: string) => Promise<ChatResponse>;
33
+ }>;
@@ -0,0 +1,4 @@
1
+ // ---------------------------------------------------------------------------
2
+ // LLM Provider interface — model-agnostic adapter
3
+ // ---------------------------------------------------------------------------
4
+ export {};
@@ -0,0 +1,9 @@
1
+ import type { SellerConfig } from './config.js';
2
+ export type DaemonLogger = Readonly<{
3
+ info: (msg: string) => void;
4
+ error: (msg: string) => void;
5
+ }>;
6
+ export declare function startSellerDaemon(config: SellerConfig, opts?: {
7
+ logger?: DaemonLogger;
8
+ signal?: AbortSignal;
9
+ }): Promise<void>;