@zhijiewang/openharness 2.1.0 → 2.3.1
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 +4 -4
- package/dist/DeferredTool.js +3 -1
- package/dist/Tool.d.ts +1 -1
- package/dist/agents/roles.js +58 -62
- package/dist/commands/cybergotchi.d.ts +1 -1
- package/dist/commands/cybergotchi.js +30 -30
- package/dist/commands/index.js +288 -132
- package/dist/components/App.d.ts +1 -1
- package/dist/components/App.js +6 -6
- package/dist/components/CompanionFooter.d.ts +1 -1
- package/dist/components/CompanionFooter.js +6 -8
- package/dist/components/CybergotchiBubble.js +5 -5
- package/dist/components/CybergotchiPanel.d.ts +1 -1
- package/dist/components/CybergotchiPanel.js +7 -7
- package/dist/components/CybergotchiPanelConnected.js +2 -2
- package/dist/components/CybergotchiSetup.js +26 -24
- package/dist/components/CybergotchiSprite.d.ts +1 -1
- package/dist/components/CybergotchiSprite.js +8 -12
- package/dist/components/DiffView.d.ts +1 -1
- package/dist/components/DiffView.js +10 -10
- package/dist/components/ErrorBoundary.d.ts +1 -1
- package/dist/components/ErrorBoundary.js +1 -1
- package/dist/components/InitWizard.js +65 -33
- package/dist/components/Markdown.js +2 -4
- package/dist/components/Messages.js +4 -4
- package/dist/components/PermissionPrompt.d.ts +1 -1
- package/dist/components/PermissionPrompt.js +15 -17
- package/dist/components/REPL.d.ts +1 -1
- package/dist/components/REPL.js +74 -49
- package/dist/components/Spinner.js +2 -2
- package/dist/components/TextInput.js +35 -29
- package/dist/components/ToolCallDisplay.js +3 -5
- package/dist/cybergotchi/bones.d.ts +1 -1
- package/dist/cybergotchi/bones.js +8 -8
- package/dist/cybergotchi/config.d.ts +2 -2
- package/dist/cybergotchi/config.js +13 -13
- package/dist/cybergotchi/events.d.ts +5 -5
- package/dist/cybergotchi/events.js +7 -7
- package/dist/cybergotchi/needs.d.ts +2 -2
- package/dist/cybergotchi/needs.js +7 -9
- package/dist/cybergotchi/personality.d.ts +2 -2
- package/dist/cybergotchi/personality.js +2 -2
- package/dist/cybergotchi/species.d.ts +1 -1
- package/dist/cybergotchi/species.js +145 -217
- package/dist/cybergotchi/speech.d.ts +2 -2
- package/dist/cybergotchi/speech.js +43 -43
- package/dist/cybergotchi/types.d.ts +4 -4
- package/dist/cybergotchi/types.js +26 -26
- package/dist/cybergotchi/useCybergotchi.d.ts +1 -1
- package/dist/cybergotchi/useCybergotchi.js +29 -25
- package/dist/git/index.js +11 -9
- package/dist/harness/checkpoints.js +29 -21
- package/dist/harness/config.d.ts +3 -3
- package/dist/harness/config.js +15 -9
- package/dist/harness/context-warning.d.ts +1 -1
- package/dist/harness/context-warning.js +1 -1
- package/dist/harness/cost.js +1 -1
- package/dist/harness/credentials.js +13 -13
- package/dist/harness/hooks.js +7 -5
- package/dist/harness/keybindings.js +20 -18
- package/dist/harness/marketplace.d.ts +3 -3
- package/dist/harness/marketplace.js +55 -42
- package/dist/harness/memory.d.ts +23 -5
- package/dist/harness/memory.js +142 -41
- package/dist/harness/onboarding.js +30 -10
- package/dist/harness/plugins.d.ts +9 -1
- package/dist/harness/plugins.js +54 -30
- package/dist/harness/rules.js +12 -7
- package/dist/harness/sandbox.js +15 -15
- package/dist/harness/session-db.d.ts +55 -0
- package/dist/harness/session-db.js +165 -0
- package/dist/harness/session.d.ts +1 -1
- package/dist/harness/session.js +34 -15
- package/dist/harness/store.d.ts +3 -3
- package/dist/harness/store.js +6 -4
- package/dist/harness/submit-handler.d.ts +4 -4
- package/dist/harness/submit-handler.js +25 -23
- package/dist/harness/telemetry.d.ts +1 -1
- package/dist/harness/telemetry.js +23 -19
- package/dist/harness/traces.d.ts +2 -2
- package/dist/harness/traces.js +39 -33
- package/dist/harness/verification.d.ts +1 -1
- package/dist/harness/verification.js +50 -44
- package/dist/lsp/client.js +44 -40
- package/dist/main.js +114 -59
- package/dist/mcp/DeferredMcpTool.d.ts +4 -4
- package/dist/mcp/DeferredMcpTool.js +9 -5
- package/dist/mcp/McpTool.d.ts +4 -4
- package/dist/mcp/McpTool.js +8 -4
- package/dist/mcp/client.d.ts +2 -2
- package/dist/mcp/client.js +21 -21
- package/dist/mcp/loader.d.ts +1 -1
- package/dist/mcp/loader.js +17 -12
- package/dist/mcp/registry.d.ts +3 -3
- package/dist/mcp/registry.js +97 -97
- package/dist/mcp/schema.d.ts +1 -1
- package/dist/mcp/schema.js +16 -16
- package/dist/mcp/server.d.ts +1 -1
- package/dist/mcp/server.js +21 -21
- package/dist/mcp/types.d.ts +3 -3
- package/dist/providers/anthropic.d.ts +2 -2
- package/dist/providers/anthropic.js +10 -9
- package/dist/providers/base.d.ts +1 -1
- package/dist/providers/index.js +10 -3
- package/dist/providers/llamacpp.d.ts +2 -2
- package/dist/providers/llamacpp.js +1 -3
- package/dist/providers/ollama.d.ts +2 -2
- package/dist/providers/ollama.js +3 -4
- package/dist/providers/openai.d.ts +2 -2
- package/dist/providers/openai.js +3 -5
- package/dist/providers/openrouter.d.ts +2 -2
- package/dist/providers/router.d.ts +1 -1
- package/dist/providers/router.js +7 -7
- package/dist/query/compress.d.ts +2 -2
- package/dist/query/compress.js +22 -21
- package/dist/query/context-manager.d.ts +1 -1
- package/dist/query/context-manager.js +5 -5
- package/dist/query/errors.js +1 -1
- package/dist/query/index.d.ts +1 -1
- package/dist/query/index.js +42 -24
- package/dist/query/tools.js +15 -12
- package/dist/query/types.d.ts +3 -1
- package/dist/query.d.ts +1 -1
- package/dist/query.js +1 -1
- package/dist/remote/auth.d.ts +2 -2
- package/dist/remote/auth.js +8 -8
- package/dist/remote/server.d.ts +3 -3
- package/dist/remote/server.js +60 -60
- package/dist/renderer/cells.js +9 -9
- package/dist/renderer/colors.js +24 -6
- package/dist/renderer/diff.d.ts +2 -2
- package/dist/renderer/diff.js +27 -19
- package/dist/renderer/differ.d.ts +1 -1
- package/dist/renderer/differ.js +9 -9
- package/dist/renderer/image.js +19 -19
- package/dist/renderer/index.d.ts +6 -6
- package/dist/renderer/index.js +163 -93
- package/dist/renderer/input.js +66 -48
- package/dist/renderer/layout.d.ts +6 -6
- package/dist/renderer/layout.js +163 -124
- package/dist/renderer/markdown.d.ts +2 -2
- package/dist/renderer/markdown.js +173 -54
- package/dist/renderer/session-browser.d.ts +2 -2
- package/dist/renderer/session-browser.js +19 -21
- package/dist/repl.d.ts +5 -5
- package/dist/repl.js +311 -198
- package/dist/sdk/index.d.ts +5 -5
- package/dist/sdk/index.js +32 -26
- package/dist/services/AgentDispatcher.d.ts +3 -3
- package/dist/services/AgentDispatcher.js +33 -29
- package/dist/services/CronExecutor.d.ts +4 -4
- package/dist/services/CronExecutor.js +12 -8
- package/dist/services/EvaluatorLoop.d.ts +3 -3
- package/dist/services/EvaluatorLoop.js +29 -21
- package/dist/services/MetaHarness.d.ts +1 -1
- package/dist/services/MetaHarness.js +34 -32
- package/dist/services/PipelineExecutor.d.ts +1 -1
- package/dist/services/PipelineExecutor.js +23 -25
- package/dist/services/SkillExtractor.d.ts +43 -0
- package/dist/services/SkillExtractor.js +163 -0
- package/dist/services/StreamingToolExecutor.d.ts +2 -2
- package/dist/services/StreamingToolExecutor.js +11 -7
- package/dist/services/a2a.d.ts +8 -8
- package/dist/services/a2a.js +44 -34
- package/dist/services/agent-messaging.d.ts +33 -15
- package/dist/services/agent-messaging.js +65 -13
- package/dist/services/cron.js +16 -16
- package/dist/tools/AgentTool/index.d.ts +5 -2
- package/dist/tools/AgentTool/index.js +25 -39
- package/dist/tools/AskUserTool/index.js +1 -1
- package/dist/tools/BashTool/index.d.ts +2 -2
- package/dist/tools/BashTool/index.js +18 -10
- package/dist/tools/CronTool/index.js +30 -12
- package/dist/tools/DiagnosticsTool/index.js +28 -22
- package/dist/tools/EnterPlanModeTool/index.js +93 -14
- package/dist/tools/EnterWorktreeTool/index.js +7 -3
- package/dist/tools/ExitPlanModeTool/index.d.ts +22 -1
- package/dist/tools/ExitPlanModeTool/index.js +20 -5
- package/dist/tools/ExitWorktreeTool/index.js +11 -4
- package/dist/tools/FileEditTool/index.js +3 -5
- package/dist/tools/FileReadTool/index.js +16 -10
- package/dist/tools/FileWriteTool/index.js +2 -2
- package/dist/tools/GlobTool/index.js +5 -9
- package/dist/tools/GrepTool/index.d.ts +2 -2
- package/dist/tools/GrepTool/index.js +14 -9
- package/dist/tools/ImageReadTool/index.js +2 -2
- package/dist/tools/KillProcessTool/index.js +11 -7
- package/dist/tools/LSTool/index.js +3 -3
- package/dist/tools/MemoryTool/index.d.ts +5 -5
- package/dist/tools/MemoryTool/index.js +28 -14
- package/dist/tools/MonitorTool/index.js +24 -19
- package/dist/tools/MultiEditTool/index.js +9 -5
- package/dist/tools/NotebookEditTool/index.js +3 -3
- package/dist/tools/ParallelAgentTool/index.d.ts +4 -4
- package/dist/tools/ParallelAgentTool/index.js +12 -6
- package/dist/tools/PipelineTool/index.js +3 -3
- package/dist/tools/PowerShellTool/index.js +10 -6
- package/dist/tools/RemoteTriggerTool/index.js +8 -4
- package/dist/tools/ScheduleWakeupTool/index.d.ts +42 -0
- package/dist/tools/ScheduleWakeupTool/index.js +115 -0
- package/dist/tools/SendMessageTool/index.js +25 -7
- package/dist/tools/SessionSearchTool/index.d.ts +15 -0
- package/dist/tools/SessionSearchTool/index.js +36 -0
- package/dist/tools/SkillTool/index.d.ts +3 -0
- package/dist/tools/SkillTool/index.js +39 -9
- package/dist/tools/TaskCreateTool/index.d.ts +2 -2
- package/dist/tools/TaskCreateTool/index.js +2 -2
- package/dist/tools/TaskGetTool/index.js +2 -2
- package/dist/tools/TaskListTool/index.js +3 -5
- package/dist/tools/TaskOutputTool/index.js +2 -2
- package/dist/tools/TaskStopTool/index.js +3 -3
- package/dist/tools/TaskUpdateTool/index.d.ts +4 -4
- package/dist/tools/TaskUpdateTool/index.js +2 -2
- package/dist/tools/ToolSearchTool/index.js +9 -6
- package/dist/tools/WebFetchTool/index.js +1 -1
- package/dist/tools/WebSearchTool/index.js +2 -6
- package/dist/tools.js +31 -30
- package/dist/types/permissions.js +15 -9
- package/dist/utils/bash-safety.d.ts +1 -1
- package/dist/utils/bash-safety.js +64 -54
- package/dist/utils/diff-algorithm.d.ts +3 -3
- package/dist/utils/diff-algorithm.js +7 -7
- package/dist/utils/fs.js +3 -3
- package/dist/utils/safe-env.js +1 -1
- package/dist/utils/theme-data.d.ts +1 -1
- package/dist/utils/theme-data.js +1 -1
- package/dist/utils/theme.d.ts +1 -1
- package/dist/utils/theme.js +1 -1
- package/dist/utils/tool-summary.d.ts +1 -1
- package/dist/utils/tool-summary.js +27 -9
- package/package.json +10 -3
package/dist/query/index.js
CHANGED
|
@@ -7,22 +7,21 @@
|
|
|
7
7
|
* - errors.ts — error classification and recovery
|
|
8
8
|
* - types.ts — shared types
|
|
9
9
|
*/
|
|
10
|
-
import { toolToAPIFormat } from "../Tool.js";
|
|
11
10
|
import { DeferredTool } from "../DeferredTool.js";
|
|
12
|
-
import { ContextManager } from "./context-manager.js";
|
|
13
|
-
import { createAssistantMessage, createUserMessage } from "../types/message.js";
|
|
14
|
-
import { StreamingToolExecutor } from "../services/StreamingToolExecutor.js";
|
|
15
11
|
import { getContextWindow } from "../harness/cost.js";
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
12
|
+
import { StreamingToolExecutor } from "../services/StreamingToolExecutor.js";
|
|
13
|
+
import { toolToAPIFormat } from "../Tool.js";
|
|
14
|
+
import { createAssistantMessage, createToolResultMessage, createUserMessage } from "../types/message.js";
|
|
15
|
+
import { compressMessages, estimateMessagesTokens, makeTokenEstimator, summarizeConversation } from "./compress.js";
|
|
16
|
+
import { ContextManager } from "./context-manager.js";
|
|
17
|
+
import { isNetworkError, isOverloadError, isPromptTooLongError, isRateLimitError, MAX_CONSECUTIVE_ERRORS, MAX_RATE_LIMIT_RETRIES, } from "./errors.js";
|
|
18
18
|
import { executeToolCalls } from "./tools.js";
|
|
19
|
-
import { isRateLimitError, isOverloadError, isPromptTooLongError, isNetworkError, MAX_CONSECUTIVE_ERRORS, MAX_RATE_LIMIT_RETRIES } from "./errors.js";
|
|
20
19
|
export { compressMessages } from "./compress.js";
|
|
21
20
|
const DEFAULT_MAX_TURNS = 50;
|
|
22
21
|
export async function* query(userMessage, config, existingMessages = []) {
|
|
23
22
|
const maxTurns = config.maxTurns ?? DEFAULT_MAX_TURNS;
|
|
24
23
|
const toolContext = {
|
|
25
|
-
workingDir: process.cwd(),
|
|
24
|
+
workingDir: config.workingDir ?? process.cwd(),
|
|
26
25
|
abortSignal: config.abortSignal,
|
|
27
26
|
provider: config.provider,
|
|
28
27
|
model: config.model,
|
|
@@ -34,22 +33,30 @@ export async function* query(userMessage, config, existingMessages = []) {
|
|
|
34
33
|
const estimateTokens = makeTokenEstimator(config.provider);
|
|
35
34
|
const contextManager = new ContextManager(undefined, config.model);
|
|
36
35
|
// Check provider capabilities
|
|
37
|
-
const modelInfo = config.provider.getModelInfo?.(config.model ??
|
|
36
|
+
const modelInfo = config.provider.getModelInfo?.(config.model ?? "");
|
|
38
37
|
const toolsSupported = !modelInfo || modelInfo.supportsTools;
|
|
39
38
|
const apiTools = toolsSupported ? config.tools.map(toolToAPIFormat) : undefined;
|
|
40
|
-
let toolPrompts = toolsSupported
|
|
41
|
-
? config.tools.map((t) => t.prompt()).join("\n\n")
|
|
42
|
-
: "";
|
|
39
|
+
let toolPrompts = toolsSupported ? config.tools.map((t) => t.prompt()).join("\n\n") : "";
|
|
43
40
|
// Hint about deferred tools available via ToolSearch
|
|
44
41
|
if (toolsSupported) {
|
|
45
|
-
const deferredCount = config.tools.filter(t => t instanceof DeferredTool && !t.activated).length;
|
|
42
|
+
const deferredCount = config.tools.filter((t) => t instanceof DeferredTool && !t.activated).length;
|
|
46
43
|
if (deferredCount > 0) {
|
|
47
44
|
toolPrompts += `\n\n[${deferredCount} additional tools available — use ToolSearch to discover them]`;
|
|
48
45
|
}
|
|
49
46
|
}
|
|
50
|
-
|
|
51
|
-
? config.systemPrompt
|
|
47
|
+
let fullSystemPrompt = toolPrompts
|
|
48
|
+
? `${config.systemPrompt}\n\n# Available Tools\n\n${toolPrompts}`
|
|
52
49
|
: config.systemPrompt;
|
|
50
|
+
// Auto-trigger skills matching user message
|
|
51
|
+
try {
|
|
52
|
+
const { findTriggeredSkills } = await import("../harness/plugins.js");
|
|
53
|
+
const triggered = findTriggeredSkills(userMessage);
|
|
54
|
+
if (triggered.length > 0) {
|
|
55
|
+
const hints = triggered.map((s) => `- **${s.name}**: ${s.description}`).join("\n");
|
|
56
|
+
fullSystemPrompt += `\n\n# Suggested Skills\nThese skills match your request. Use Skill tool to load them:\n${hints}`;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch { /* skills optional */ }
|
|
53
60
|
const state = {
|
|
54
61
|
messages: [...existingMessages, createUserMessage(userMessage)],
|
|
55
62
|
turn: 0,
|
|
@@ -81,7 +88,9 @@ export async function* query(userMessage, config, existingMessages = []) {
|
|
|
81
88
|
state.messages = await summarizeConversation(config.provider, state.messages, config.model, Math.floor(contextWindow * 0.5));
|
|
82
89
|
yield { type: "error", message: "Context compressed with LLM summarization." };
|
|
83
90
|
}
|
|
84
|
-
catch {
|
|
91
|
+
catch {
|
|
92
|
+
/* continue with basic compression */
|
|
93
|
+
}
|
|
85
94
|
}
|
|
86
95
|
}
|
|
87
96
|
// ── LLM call with streaming ──
|
|
@@ -131,7 +140,10 @@ export async function* query(userMessage, config, existingMessages = []) {
|
|
|
131
140
|
state.consecutiveErrors++;
|
|
132
141
|
// Circuit breaker
|
|
133
142
|
if (state.consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {
|
|
134
|
-
yield {
|
|
143
|
+
yield {
|
|
144
|
+
type: "error",
|
|
145
|
+
message: `Too many consecutive errors (${state.consecutiveErrors}): ${streamError.message}`,
|
|
146
|
+
};
|
|
135
147
|
yield { type: "turn_complete", reason: "error" };
|
|
136
148
|
return;
|
|
137
149
|
}
|
|
@@ -140,13 +152,16 @@ export async function* query(userMessage, config, existingMessages = []) {
|
|
|
140
152
|
const attempt = state.consecutiveErrors;
|
|
141
153
|
const isOverload = isOverloadError(streamError);
|
|
142
154
|
if (attempt <= MAX_RATE_LIMIT_RETRIES) {
|
|
143
|
-
const baseRetry =
|
|
155
|
+
const baseRetry = 2 ** attempt * (isOverload ? 2 : 1);
|
|
144
156
|
const retryIn = baseRetry * (0.5 + Math.random());
|
|
145
157
|
yield { type: "rate_limited", retryIn: Math.round(retryIn), attempt };
|
|
146
158
|
await new Promise((r) => setTimeout(r, retryIn * 1000));
|
|
147
159
|
continue;
|
|
148
160
|
}
|
|
149
|
-
yield {
|
|
161
|
+
yield {
|
|
162
|
+
type: "error",
|
|
163
|
+
message: `${isOverload ? "Server overloaded" : "Rate limit exceeded"} after ${MAX_RATE_LIMIT_RETRIES} retries.`,
|
|
164
|
+
};
|
|
150
165
|
yield { type: "turn_complete", reason: "error" };
|
|
151
166
|
return;
|
|
152
167
|
}
|
|
@@ -164,7 +179,7 @@ export async function* query(userMessage, config, existingMessages = []) {
|
|
|
164
179
|
}
|
|
165
180
|
if (isNetworkError(streamError)) {
|
|
166
181
|
state.transition = "retry_network";
|
|
167
|
-
const delay = 1000 *
|
|
182
|
+
const delay = 1000 * 2 ** (state.consecutiveErrors - 1);
|
|
168
183
|
yield { type: "error", message: `Network error, retrying in ${delay / 1000}s...` };
|
|
169
184
|
await new Promise((r) => setTimeout(r, delay));
|
|
170
185
|
continue;
|
|
@@ -178,7 +193,10 @@ export async function* query(userMessage, config, existingMessages = []) {
|
|
|
178
193
|
return;
|
|
179
194
|
}
|
|
180
195
|
if (assistantContent === "" && toolCalls.length === 0) {
|
|
181
|
-
yield {
|
|
196
|
+
yield {
|
|
197
|
+
type: "error",
|
|
198
|
+
message: "No response received. Check that your model server is running and the model name is correct.",
|
|
199
|
+
};
|
|
182
200
|
return;
|
|
183
201
|
}
|
|
184
202
|
state.messages.push(createAssistantMessage(assistantContent, toolCalls.length > 0 ? toolCalls : undefined));
|
|
@@ -189,9 +207,9 @@ export async function* query(userMessage, config, existingMessages = []) {
|
|
|
189
207
|
// Collect streaming tool results
|
|
190
208
|
await streamingExecutor.waitForAll();
|
|
191
209
|
const completedResults = [...streamingExecutor.getCompletedResults()];
|
|
192
|
-
const executedIds = new Set(completedResults.map(r => r.toolCall.id));
|
|
210
|
+
const executedIds = new Set(completedResults.map((r) => r.toolCall.id));
|
|
193
211
|
for (const { callId, chunk } of streamingExecutor.outputChunks) {
|
|
194
|
-
yield { type:
|
|
212
|
+
yield { type: "tool_output_delta", callId, chunk };
|
|
195
213
|
}
|
|
196
214
|
for (const { toolCall: tc, result } of completedResults) {
|
|
197
215
|
yield { type: "tool_call_end", callId: tc.id, output: result.output, isError: result.isError };
|
|
@@ -200,7 +218,7 @@ export async function* query(userMessage, config, existingMessages = []) {
|
|
|
200
218
|
state.messages.push(createToolResultMessage({ callId: tc.id, output: budgetedOutput, isError: result.isError }));
|
|
201
219
|
}
|
|
202
220
|
// Execute remaining tools not started during streaming
|
|
203
|
-
const remaining = toolCalls.filter(tc => !executedIds.has(tc.id));
|
|
221
|
+
const remaining = toolCalls.filter((tc) => !executedIds.has(tc.id));
|
|
204
222
|
if (remaining.length > 0) {
|
|
205
223
|
yield* executeToolCalls(remaining, config.tools, toolContext, config.permissionMode, config.askUser, state);
|
|
206
224
|
}
|
package/dist/query/tools.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tool execution — permission checking, batching, output capping.
|
|
3
3
|
*/
|
|
4
|
+
import { createCheckpoint, getAffectedFiles } from "../harness/checkpoints.js";
|
|
5
|
+
import { emitHook } from "../harness/hooks.js";
|
|
4
6
|
import { findToolByName } from "../Tool.js";
|
|
5
7
|
import { createToolResultMessage } from "../types/message.js";
|
|
6
8
|
import { checkPermission } from "../types/permissions.js";
|
|
7
|
-
import { emitHook } from "../harness/hooks.js";
|
|
8
|
-
import { createCheckpoint, getAffectedFiles } from "../harness/checkpoints.js";
|
|
9
9
|
const MAX_TOOL_RESULT_CHARS = 100_000;
|
|
10
10
|
const TOOL_TIMEOUT_MS = 120_000;
|
|
11
11
|
export function partitionToolCalls(toolCalls, tools) {
|
|
@@ -86,17 +86,17 @@ export async function executeSingleTool(toolCall, tools, context, permissionMode
|
|
|
86
86
|
toolOutput: result.output.slice(0, 1000),
|
|
87
87
|
});
|
|
88
88
|
// Emit fileChanged hook for file-modifying tools
|
|
89
|
-
if (!result.isError && [
|
|
89
|
+
if (!result.isError && ["Edit", "Write", "MultiEdit"].includes(tool.name)) {
|
|
90
90
|
const filePaths = getAffectedFiles(tool.name, parsed.data);
|
|
91
91
|
for (const fp of filePaths) {
|
|
92
92
|
emitHook("fileChanged", { filePath: fp, toolName: tool.name });
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
95
|
// Verification loop: auto-run lint/typecheck after file-modifying tools
|
|
96
|
-
let verificationSuffix =
|
|
97
|
-
if (!result.isError && [
|
|
96
|
+
let verificationSuffix = "";
|
|
97
|
+
if (!result.isError && ["Edit", "Write", "MultiEdit"].includes(tool.name)) {
|
|
98
98
|
try {
|
|
99
|
-
const { runVerificationForFiles, getVerificationConfig, extractFilePaths } = await import(
|
|
99
|
+
const { runVerificationForFiles, getVerificationConfig, extractFilePaths } = await import("../harness/verification.js");
|
|
100
100
|
const vConfig = getVerificationConfig();
|
|
101
101
|
if (vConfig?.enabled) {
|
|
102
102
|
const filePaths = extractFilePaths(tool.name, parsed.data);
|
|
@@ -105,24 +105,27 @@ export async function executeSingleTool(toolCall, tools, context, permissionMode
|
|
|
105
105
|
if (vResult.ran) {
|
|
106
106
|
if (!vResult.passed) {
|
|
107
107
|
verificationSuffix = `\n\n[Verification FAILED]\n${vResult.summary}`;
|
|
108
|
-
if (vConfig.mode ===
|
|
108
|
+
if (vConfig.mode === "block") {
|
|
109
109
|
result = { output: result.output, isError: true };
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
112
|
else {
|
|
113
|
-
verificationSuffix =
|
|
113
|
+
verificationSuffix = "\n\n[Verification passed]";
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
|
-
catch {
|
|
119
|
+
catch {
|
|
120
|
+
/* verification should never break tool execution */
|
|
121
|
+
}
|
|
120
122
|
}
|
|
121
123
|
// Strip ANSI and cap output, then append verification suffix
|
|
122
124
|
let output = result.output.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "") + verificationSuffix;
|
|
123
125
|
if (output.length > MAX_TOOL_RESULT_CHARS) {
|
|
124
|
-
output =
|
|
125
|
-
|
|
126
|
+
output =
|
|
127
|
+
output.slice(0, MAX_TOOL_RESULT_CHARS) +
|
|
128
|
+
`\n\n[TRUNCATED: output was ${output.length.toLocaleString()} chars, showing first ${MAX_TOOL_RESULT_CHARS.toLocaleString()}]`;
|
|
126
129
|
}
|
|
127
130
|
return { output, isError: result.isError };
|
|
128
131
|
}
|
|
@@ -134,7 +137,7 @@ export async function* executeToolCalls(toolCalls, tools, context, permissionMod
|
|
|
134
137
|
const batches = partitionToolCalls(toolCalls, tools);
|
|
135
138
|
const outputChunks = [];
|
|
136
139
|
const onOutputChunk = (callId, chunk) => {
|
|
137
|
-
outputChunks.push({ type:
|
|
140
|
+
outputChunks.push({ type: "tool_output_delta", callId, chunk });
|
|
138
141
|
};
|
|
139
142
|
for (const batch of batches) {
|
|
140
143
|
if (batch.concurrent) {
|
package/dist/query/types.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared types for the query loop sub-modules.
|
|
3
3
|
*/
|
|
4
|
-
import type { Message } from "../types/message.js";
|
|
5
4
|
import type { Provider } from "../providers/base.js";
|
|
6
5
|
import type { Tools } from "../Tool.js";
|
|
6
|
+
import type { Message } from "../types/message.js";
|
|
7
7
|
import type { AskUserFn, PermissionMode } from "../types/permissions.js";
|
|
8
8
|
export type QueryConfig = {
|
|
9
9
|
provider: Provider;
|
|
@@ -16,6 +16,8 @@ export type QueryConfig = {
|
|
|
16
16
|
maxCost?: number;
|
|
17
17
|
model?: string;
|
|
18
18
|
abortSignal?: AbortSignal;
|
|
19
|
+
/** Working directory for tool execution (defaults to process.cwd()) */
|
|
20
|
+
workingDir?: string;
|
|
19
21
|
};
|
|
20
22
|
export type TransitionReason = "next_turn" | "retry_network" | "retry_prompt_too_long" | "retry_max_output_tokens";
|
|
21
23
|
export type QueryLoopState = {
|
package/dist/query.d.ts
CHANGED
|
@@ -6,6 +6,6 @@
|
|
|
6
6
|
* - query/errors.ts — error classification and recovery
|
|
7
7
|
* - query/types.ts — shared types
|
|
8
8
|
*/
|
|
9
|
-
export { query, compressMessages } from "./query/index.js";
|
|
10
9
|
export type { QueryConfig, QueryLoopState } from "./query/index.js";
|
|
10
|
+
export { compressMessages, query } from "./query/index.js";
|
|
11
11
|
//# sourceMappingURL=query.d.ts.map
|
package/dist/query.js
CHANGED
package/dist/remote/auth.d.ts
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* API security layer — auth, rate limiting, and tool access control
|
|
3
3
|
* for the remote server.
|
|
4
4
|
*/
|
|
5
|
-
import type { IncomingMessage, ServerResponse } from
|
|
6
|
-
import type { Tools } from
|
|
5
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
6
|
+
import type { Tools } from "../Tool.js";
|
|
7
7
|
/** Check if a request is within rate limits. Returns true if allowed. */
|
|
8
8
|
export declare function checkRateLimit(ip: string, maxPerMinute: number): boolean;
|
|
9
9
|
/** Validate a bearer token against configured tokens. Returns true if valid. */
|
package/dist/remote/auth.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* API security layer — auth, rate limiting, and tool access control
|
|
3
3
|
* for the remote server.
|
|
4
4
|
*/
|
|
5
|
-
import { readOhConfig } from
|
|
5
|
+
import { readOhConfig } from "../harness/config.js";
|
|
6
6
|
const rateLimitMap = new Map();
|
|
7
7
|
const WINDOW_MS = 60_000; // 1 minute sliding window
|
|
8
8
|
/** Check if a request is within rate limits. Returns true if allowed. */
|
|
@@ -28,7 +28,7 @@ export function validateToken(authHeader) {
|
|
|
28
28
|
return true;
|
|
29
29
|
if (!authHeader)
|
|
30
30
|
return false;
|
|
31
|
-
const token = authHeader.replace(/^Bearer\s+/i,
|
|
31
|
+
const token = authHeader.replace(/^Bearer\s+/i, "").trim();
|
|
32
32
|
if (!token)
|
|
33
33
|
return false;
|
|
34
34
|
return tokens.includes(token);
|
|
@@ -40,8 +40,8 @@ export function filterRemoteTools(tools) {
|
|
|
40
40
|
const allowed = config?.remote?.allowedTools;
|
|
41
41
|
if (!allowed || allowed.length === 0)
|
|
42
42
|
return tools;
|
|
43
|
-
const allowSet = new Set(allowed.map(n => n.toLowerCase()));
|
|
44
|
-
const filtered = tools.filter(t => allowSet.has(t.name.toLowerCase()));
|
|
43
|
+
const allowSet = new Set(allowed.map((n) => n.toLowerCase()));
|
|
44
|
+
const filtered = tools.filter((t) => allowSet.has(t.name.toLowerCase()));
|
|
45
45
|
return filtered.length > 0 ? filtered : tools; // fallback to all if filter empties
|
|
46
46
|
}
|
|
47
47
|
// ── Request ID ──
|
|
@@ -56,17 +56,17 @@ export function generateRequestId() {
|
|
|
56
56
|
*/
|
|
57
57
|
export function authenticateRequest(req, res) {
|
|
58
58
|
const requestId = generateRequestId();
|
|
59
|
-
res.setHeader(
|
|
59
|
+
res.setHeader("X-Request-ID", requestId);
|
|
60
60
|
// Token auth
|
|
61
61
|
if (!validateToken(req.headers.authorization)) {
|
|
62
|
-
return { allowed: false, reason:
|
|
62
|
+
return { allowed: false, reason: "Invalid or missing bearer token", requestId };
|
|
63
63
|
}
|
|
64
64
|
// Rate limiting
|
|
65
65
|
const config = readOhConfig();
|
|
66
66
|
const rateLimit = config?.remote?.rateLimit ?? 60; // default 60/min
|
|
67
|
-
const ip = req.socket.remoteAddress ??
|
|
67
|
+
const ip = req.socket.remoteAddress ?? "unknown";
|
|
68
68
|
if (!checkRateLimit(ip, rateLimit)) {
|
|
69
|
-
return { allowed: false, reason:
|
|
69
|
+
return { allowed: false, reason: "Rate limit exceeded", requestId };
|
|
70
70
|
}
|
|
71
71
|
return { allowed: true, requestId };
|
|
72
72
|
}
|
package/dist/remote/server.d.ts
CHANGED
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
*
|
|
11
11
|
* Security: bearer token auth, per-IP rate limiting, tool allowlists.
|
|
12
12
|
*/
|
|
13
|
-
import type { Provider } from
|
|
14
|
-
import type { Tools } from
|
|
15
|
-
import type { PermissionMode } from
|
|
13
|
+
import type { Provider } from "../providers/base.js";
|
|
14
|
+
import type { Tools } from "../Tool.js";
|
|
15
|
+
import type { PermissionMode } from "../types/permissions.js";
|
|
16
16
|
export type RemoteServerConfig = {
|
|
17
17
|
port: number;
|
|
18
18
|
provider: Provider;
|
package/dist/remote/server.js
CHANGED
|
@@ -10,10 +10,10 @@
|
|
|
10
10
|
*
|
|
11
11
|
* Security: bearer token auth, per-IP rate limiting, tool allowlists.
|
|
12
12
|
*/
|
|
13
|
-
import { createServer } from
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
13
|
+
import { createServer } from "node:http";
|
|
14
|
+
import { WebSocket, WebSocketServer } from "ws";
|
|
15
|
+
import { createSessionCard, discoverAgents, generateMessageId, publishCard, unpublishCard, } from "../services/a2a.js";
|
|
16
|
+
import { authenticateRequest, filterRemoteTools } from "./auth.js";
|
|
17
17
|
export class RemoteServer {
|
|
18
18
|
config;
|
|
19
19
|
channels = new Map();
|
|
@@ -27,8 +27,8 @@ export class RemoteServer {
|
|
|
27
27
|
this.server = createServer((req, res) => this.handleHttp(req, res));
|
|
28
28
|
// WebSocket upgrade
|
|
29
29
|
const wss = new WebSocketServer({ noServer: true });
|
|
30
|
-
this.server.on(
|
|
31
|
-
if (request.url ===
|
|
30
|
+
this.server.on("upgrade", (request, socket, head) => {
|
|
31
|
+
if (request.url === "/channel") {
|
|
32
32
|
wss.handleUpgrade(request, socket, head, (ws) => {
|
|
33
33
|
this.handleChannel(ws);
|
|
34
34
|
});
|
|
@@ -69,29 +69,29 @@ export class RemoteServer {
|
|
|
69
69
|
}
|
|
70
70
|
async handleHttp(req, res) {
|
|
71
71
|
// CORS headers
|
|
72
|
-
res.setHeader(
|
|
73
|
-
res.setHeader(
|
|
74
|
-
res.setHeader(
|
|
75
|
-
if (req.method ===
|
|
72
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
73
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
74
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
|
|
75
|
+
if (req.method === "OPTIONS") {
|
|
76
76
|
res.writeHead(204);
|
|
77
77
|
res.end();
|
|
78
78
|
return;
|
|
79
79
|
}
|
|
80
80
|
// Auth check (skip for /status which is a health check)
|
|
81
|
-
if (req.url !==
|
|
81
|
+
if (req.url !== "/status") {
|
|
82
82
|
const auth = authenticateRequest(req, res);
|
|
83
83
|
if (!auth.allowed) {
|
|
84
|
-
const status = auth.reason?.includes(
|
|
85
|
-
res.writeHead(status, {
|
|
84
|
+
const status = auth.reason?.includes("Rate limit") ? 429 : 401;
|
|
85
|
+
res.writeHead(status, { "Content-Type": "application/json" });
|
|
86
86
|
res.end(JSON.stringify({ error: auth.reason, requestId: auth.requestId }));
|
|
87
87
|
return;
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
90
|
// ── GET /status ──
|
|
91
|
-
if (req.url ===
|
|
92
|
-
res.writeHead(200, {
|
|
91
|
+
if (req.url === "/status" && req.method === "GET") {
|
|
92
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
93
93
|
res.end(JSON.stringify({
|
|
94
|
-
status:
|
|
94
|
+
status: "ok",
|
|
95
95
|
provider: this.config.provider.name,
|
|
96
96
|
model: this.config.model,
|
|
97
97
|
channels: this.channels.size,
|
|
@@ -100,24 +100,24 @@ export class RemoteServer {
|
|
|
100
100
|
return;
|
|
101
101
|
}
|
|
102
102
|
// ── POST /dispatch ──
|
|
103
|
-
if (req.url ===
|
|
103
|
+
if (req.url === "/dispatch" && req.method === "POST") {
|
|
104
104
|
await this.handleDispatch(req, res);
|
|
105
105
|
return;
|
|
106
106
|
}
|
|
107
107
|
// ── POST /a2a ──
|
|
108
|
-
if (req.url ===
|
|
108
|
+
if (req.url === "/a2a" && req.method === "POST") {
|
|
109
109
|
await this.handleA2A(req, res);
|
|
110
110
|
return;
|
|
111
111
|
}
|
|
112
|
-
res.writeHead(404, {
|
|
113
|
-
res.end(JSON.stringify({ error:
|
|
112
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
113
|
+
res.end(JSON.stringify({ error: "Not found" }));
|
|
114
114
|
}
|
|
115
115
|
async handleDispatch(req, res) {
|
|
116
116
|
const body = await readBody(req);
|
|
117
117
|
try {
|
|
118
118
|
const { prompt, maxTurns } = JSON.parse(body);
|
|
119
119
|
if (!prompt) {
|
|
120
|
-
res.writeHead(400, {
|
|
120
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
121
121
|
res.end(JSON.stringify({ error: 'Missing "prompt" field' }));
|
|
122
122
|
return;
|
|
123
123
|
}
|
|
@@ -125,11 +125,11 @@ export class RemoteServer {
|
|
|
125
125
|
const tools = filterRemoteTools(this.config.tools);
|
|
126
126
|
// Stream response as Server-Sent Events
|
|
127
127
|
res.writeHead(200, {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
128
|
+
"Content-Type": "text/event-stream",
|
|
129
|
+
"Cache-Control": "no-cache",
|
|
130
|
+
Connection: "keep-alive",
|
|
131
131
|
});
|
|
132
|
-
const { query } = await import(
|
|
132
|
+
const { query } = await import("../query.js");
|
|
133
133
|
const config = {
|
|
134
134
|
provider: this.config.provider,
|
|
135
135
|
tools,
|
|
@@ -142,12 +142,12 @@ export class RemoteServer {
|
|
|
142
142
|
const data = JSON.stringify(event);
|
|
143
143
|
res.write(`data: ${data}\n\n`);
|
|
144
144
|
}
|
|
145
|
-
res.write(
|
|
145
|
+
res.write("data: [DONE]\n\n");
|
|
146
146
|
res.end();
|
|
147
147
|
}
|
|
148
148
|
catch (err) {
|
|
149
149
|
if (!res.headersSent) {
|
|
150
|
-
res.writeHead(500, {
|
|
150
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
151
151
|
}
|
|
152
152
|
res.end(JSON.stringify({ error: err instanceof Error ? err.message : String(err) }));
|
|
153
153
|
}
|
|
@@ -166,26 +166,26 @@ export class RemoteServer {
|
|
|
166
166
|
try {
|
|
167
167
|
const message = JSON.parse(body);
|
|
168
168
|
switch (message.payload.kind) {
|
|
169
|
-
case
|
|
169
|
+
case "discover": {
|
|
170
170
|
// Return our agent card
|
|
171
171
|
const agents = discoverAgents();
|
|
172
|
-
const self = agents.find(a => a.id === this.agentCardId);
|
|
172
|
+
const self = agents.find((a) => a.id === this.agentCardId);
|
|
173
173
|
const response = {
|
|
174
174
|
id: generateMessageId(),
|
|
175
|
-
from: this.agentCardId ??
|
|
175
|
+
from: this.agentCardId ?? "unknown",
|
|
176
176
|
to: message.from,
|
|
177
|
-
type:
|
|
178
|
-
payload: { kind:
|
|
177
|
+
type: "result",
|
|
178
|
+
payload: { kind: "result", taskId: message.id, output: self ?? { error: "agent not found" } },
|
|
179
179
|
timestamp: Date.now(),
|
|
180
180
|
};
|
|
181
|
-
res.writeHead(200, {
|
|
181
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
182
182
|
res.end(JSON.stringify(response));
|
|
183
183
|
return;
|
|
184
184
|
}
|
|
185
|
-
case
|
|
185
|
+
case "task": {
|
|
186
186
|
// Execute the task via query loop
|
|
187
187
|
const tools = filterRemoteTools(this.config.tools);
|
|
188
|
-
const { query } = await import(
|
|
188
|
+
const { query } = await import("../query.js");
|
|
189
189
|
const config = {
|
|
190
190
|
provider: this.config.provider,
|
|
191
191
|
tools,
|
|
@@ -194,45 +194,45 @@ export class RemoteServer {
|
|
|
194
194
|
model: this.config.model,
|
|
195
195
|
maxTurns: 10,
|
|
196
196
|
};
|
|
197
|
-
let output =
|
|
197
|
+
let output = "";
|
|
198
198
|
for await (const event of query(String(message.payload.input), config)) {
|
|
199
|
-
if (event.type ===
|
|
199
|
+
if (event.type === "text_delta")
|
|
200
200
|
output += event.content;
|
|
201
201
|
}
|
|
202
202
|
const response = {
|
|
203
203
|
id: generateMessageId(),
|
|
204
|
-
from: this.agentCardId ??
|
|
204
|
+
from: this.agentCardId ?? "unknown",
|
|
205
205
|
to: message.from,
|
|
206
|
-
type:
|
|
207
|
-
payload: { kind:
|
|
206
|
+
type: "result",
|
|
207
|
+
payload: { kind: "result", taskId: message.id, output },
|
|
208
208
|
timestamp: Date.now(),
|
|
209
209
|
};
|
|
210
|
-
res.writeHead(200, {
|
|
210
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
211
211
|
res.end(JSON.stringify(response));
|
|
212
212
|
return;
|
|
213
213
|
}
|
|
214
|
-
case
|
|
214
|
+
case "status": {
|
|
215
215
|
const response = {
|
|
216
216
|
id: generateMessageId(),
|
|
217
|
-
from: this.agentCardId ??
|
|
217
|
+
from: this.agentCardId ?? "unknown",
|
|
218
218
|
to: message.from,
|
|
219
|
-
type:
|
|
220
|
-
payload: { kind:
|
|
219
|
+
type: "status",
|
|
220
|
+
payload: { kind: "status", state: "idle" },
|
|
221
221
|
timestamp: Date.now(),
|
|
222
222
|
};
|
|
223
|
-
res.writeHead(200, {
|
|
223
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
224
224
|
res.end(JSON.stringify(response));
|
|
225
225
|
return;
|
|
226
226
|
}
|
|
227
227
|
default: {
|
|
228
|
-
res.writeHead(400, {
|
|
228
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
229
229
|
res.end(JSON.stringify({ error: `Unknown A2A message kind: ${message.payload.kind}` }));
|
|
230
230
|
return;
|
|
231
231
|
}
|
|
232
232
|
}
|
|
233
233
|
}
|
|
234
234
|
catch (err) {
|
|
235
|
-
res.writeHead(400, {
|
|
235
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
236
236
|
res.end(JSON.stringify({ error: err instanceof Error ? err.message : String(err) }));
|
|
237
237
|
}
|
|
238
238
|
}
|
|
@@ -242,13 +242,13 @@ export class RemoteServer {
|
|
|
242
242
|
const channel = { id, ws, abortController };
|
|
243
243
|
this.channels.set(id, channel);
|
|
244
244
|
process.stderr.write(`[remote] Channel ${id} connected\n`);
|
|
245
|
-
ws.send(JSON.stringify({ type:
|
|
246
|
-
ws.on(
|
|
245
|
+
ws.send(JSON.stringify({ type: "connected", channelId: id }));
|
|
246
|
+
ws.on("message", async (data) => {
|
|
247
247
|
try {
|
|
248
248
|
const msg = JSON.parse(data.toString());
|
|
249
|
-
if (msg.type ===
|
|
249
|
+
if (msg.type === "dispatch") {
|
|
250
250
|
const tools = filterRemoteTools(this.config.tools);
|
|
251
|
-
const { query } = await import(
|
|
251
|
+
const { query } = await import("../query.js");
|
|
252
252
|
const config = {
|
|
253
253
|
provider: this.config.provider,
|
|
254
254
|
tools,
|
|
@@ -263,18 +263,18 @@ export class RemoteServer {
|
|
|
263
263
|
break;
|
|
264
264
|
ws.send(JSON.stringify(event));
|
|
265
265
|
}
|
|
266
|
-
ws.send(JSON.stringify({ type:
|
|
266
|
+
ws.send(JSON.stringify({ type: "dispatch_complete" }));
|
|
267
267
|
}
|
|
268
|
-
if (msg.type ===
|
|
268
|
+
if (msg.type === "abort") {
|
|
269
269
|
abortController.abort();
|
|
270
|
-
ws.send(JSON.stringify({ type:
|
|
270
|
+
ws.send(JSON.stringify({ type: "aborted" }));
|
|
271
271
|
}
|
|
272
272
|
}
|
|
273
273
|
catch (err) {
|
|
274
|
-
ws.send(JSON.stringify({ type:
|
|
274
|
+
ws.send(JSON.stringify({ type: "error", message: err instanceof Error ? err.message : String(err) }));
|
|
275
275
|
}
|
|
276
276
|
});
|
|
277
|
-
ws.on(
|
|
277
|
+
ws.on("close", () => {
|
|
278
278
|
abortController.abort();
|
|
279
279
|
this.channels.delete(id);
|
|
280
280
|
process.stderr.write(`[remote] Channel ${id} disconnected\n`);
|
|
@@ -284,9 +284,9 @@ export class RemoteServer {
|
|
|
284
284
|
function readBody(req) {
|
|
285
285
|
return new Promise((resolve, reject) => {
|
|
286
286
|
const chunks = [];
|
|
287
|
-
req.on(
|
|
288
|
-
req.on(
|
|
289
|
-
req.on(
|
|
287
|
+
req.on("data", (chunk) => chunks.push(chunk));
|
|
288
|
+
req.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
|
|
289
|
+
req.on("error", reject);
|
|
290
290
|
});
|
|
291
291
|
}
|
|
292
292
|
//# sourceMappingURL=server.js.map
|