@oh-my-pi/pi-coding-agent 6.7.670 → 6.8.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/CHANGELOG.md +28 -0
- package/package.json +6 -7
- package/src/cli/session-picker.ts +27 -28
- package/src/cli/setup-cli.ts +7 -16
- package/src/cli/update-cli.ts +1 -1
- package/src/config.ts +1 -1
- package/src/core/agent-session.ts +202 -37
- package/src/core/agent-storage.ts +1 -1
- package/src/core/auth-storage.ts +15 -25
- package/src/core/bash-executor.ts +63 -105
- package/src/core/custom-commands/loader.ts +1 -1
- package/src/core/custom-tools/loader.ts +1 -1
- package/src/core/custom-tools/types.ts +1 -2
- package/src/core/exec.ts +16 -100
- package/src/core/extensions/index.ts +1 -7
- package/src/core/extensions/loader.ts +1 -1
- package/src/core/extensions/runner.ts +1 -1
- package/src/core/extensions/types.ts +2 -2
- package/src/core/extensions/wrapper.ts +15 -20
- package/src/core/frontmatter.ts +1 -1
- package/src/core/history-storage.ts +3 -6
- package/src/core/hooks/index.ts +2 -2
- package/src/core/hooks/loader.ts +1 -1
- package/src/core/hooks/tool-wrapper.ts +14 -26
- package/src/core/hooks/types.ts +1 -2
- package/src/core/keybindings.ts +1 -1
- package/src/core/mcp/client.ts +13 -13
- package/src/core/mcp/json-rpc.ts +1 -1
- package/src/core/mcp/loader.ts +1 -1
- package/src/core/mcp/manager.ts +2 -2
- package/src/core/mcp/tool-cache.ts +1 -1
- package/src/core/mcp/transports/http.ts +32 -70
- package/src/core/model-registry.ts +1 -1
- package/src/core/plugins/installer.ts +13 -11
- package/src/core/prompt-templates.ts +4 -9
- package/src/core/python-executor.ts +23 -18
- package/src/core/python-gateway-coordinator.ts +29 -28
- package/src/core/python-kernel.ts +230 -211
- package/src/core/sdk.ts +10 -13
- package/src/core/session-manager.ts +1 -1
- package/src/core/settings-manager.ts +22 -9
- package/src/core/skills.ts +1 -1
- package/src/core/ssh/connection-manager.ts +19 -33
- package/src/core/ssh/ssh-executor.ts +39 -35
- package/src/core/ssh/sshfs-mount.ts +14 -33
- package/src/core/storage-migration.ts +1 -1
- package/src/core/streaming-output.ts +183 -127
- package/src/core/system-prompt.ts +119 -79
- package/src/core/title-generator.ts +1 -1
- package/src/core/tools/ask.ts +2 -2
- package/src/core/tools/bash.ts +3 -3
- package/src/core/tools/calculator.ts +1 -1
- package/src/core/tools/exa/mcp-client.ts +1 -1
- package/src/core/tools/exa/render.ts +1 -1
- package/src/core/tools/find.ts +39 -71
- package/src/core/tools/gemini-image.ts +1 -1
- package/src/core/tools/grep.ts +88 -100
- package/src/core/tools/index.ts +1 -1
- package/src/core/tools/ls.ts +1 -1
- package/src/core/tools/lsp/client.ts +50 -50
- package/src/core/tools/lsp/clients/lsp-linter-client.ts +1 -1
- package/src/core/tools/lsp/config.ts +1 -1
- package/src/core/tools/lsp/index.ts +2 -4
- package/src/core/tools/lsp/lspmux.ts +1 -1
- package/src/core/tools/lsp/rust-analyzer.ts +2 -2
- package/src/core/tools/lsp/utils.ts +0 -14
- package/src/core/tools/notebook.ts +1 -1
- package/src/core/tools/patch/shared.ts +3 -4
- package/src/core/tools/python.ts +3 -3
- package/src/core/tools/read.ts +29 -68
- package/src/core/tools/render-utils.ts +0 -5
- package/src/core/tools/ssh.ts +3 -3
- package/src/core/tools/task/model-resolver.ts +7 -9
- package/src/core/tools/task/worker.ts +144 -139
- package/src/core/tools/todo-write.ts +1 -1
- package/src/core/tools/truncate.ts +2 -2
- package/src/core/tools/web-fetch.ts +13 -15
- package/src/core/tools/web-scrapers/types.ts +1 -3
- package/src/core/tools/web-scrapers/utils.ts +14 -13
- package/src/core/tools/web-scrapers/youtube.ts +39 -12
- package/src/core/tools/web-search/auth.ts +1 -1
- package/src/core/tools/write.ts +1 -1
- package/src/core/ttsr.ts +1 -1
- package/src/core/utils.ts +1 -187
- package/src/core/voice-controller.ts +1 -1
- package/src/core/voice-supervisor.ts +11 -38
- package/src/core/voice.ts +1 -8
- package/src/discovery/codex.ts +1 -1
- package/src/index.ts +4 -4
- package/src/main.ts +5 -10
- package/src/migrations.ts +1 -1
- package/src/modes/index.ts +7 -40
- package/src/modes/interactive/components/extensions/state-manager.ts +1 -1
- package/src/modes/interactive/components/hook-editor.ts +12 -9
- package/src/modes/interactive/components/login-dialog.ts +24 -11
- package/src/modes/interactive/components/settings-defs.ts +9 -0
- package/src/modes/interactive/components/status-line.ts +36 -35
- package/src/modes/interactive/components/todo-display.ts +1 -1
- package/src/modes/interactive/components/tool-execution.ts +1 -1
- package/src/modes/interactive/controllers/command-controller.ts +50 -84
- package/src/modes/interactive/controllers/extension-ui-controller.ts +76 -76
- package/src/modes/interactive/controllers/input-controller.ts +12 -11
- package/src/modes/interactive/interactive-mode.ts +10 -11
- package/src/modes/interactive/theme/theme.ts +1 -1
- package/src/modes/interactive/types.ts +1 -1
- package/src/modes/rpc/rpc-client.ts +91 -121
- package/src/modes/rpc/rpc-mode.ts +71 -79
- package/src/prompts/system/ttsr-interrupt.md +7 -0
- package/src/utils/clipboard.ts +57 -141
- package/src/utils/shell-snapshot.ts +12 -60
- package/src/utils/shell.ts +35 -56
- package/src/utils/tools-manager.ts +42 -71
- package/src/core/logger.ts +0 -111
- package/src/modes/cleanup.ts +0 -23
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import type { AgentTool, AgentToolContext, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
|
6
|
+
import type { Static, TSchema } from "@sinclair/typebox";
|
|
6
7
|
import type { HookRunner } from "./runner";
|
|
7
8
|
import type { ToolCallEventResult, ToolResultEventResult } from "./types";
|
|
8
9
|
|
|
@@ -14,16 +15,18 @@ import type { ToolCallEventResult, ToolResultEventResult } from "./types";
|
|
|
14
15
|
* - Emits tool_result event after execution (can modify result)
|
|
15
16
|
* - Forwards onUpdate callback to wrapped tool for progress streaming
|
|
16
17
|
*/
|
|
17
|
-
export class HookToolWrapper<
|
|
18
|
+
export class HookToolWrapper<TParameters extends TSchema = TSchema, TDetails = unknown>
|
|
19
|
+
implements AgentTool<TParameters, TDetails>
|
|
20
|
+
{
|
|
18
21
|
name: string;
|
|
19
22
|
label: string;
|
|
20
23
|
description: string;
|
|
21
|
-
parameters:
|
|
22
|
-
renderCall?: AgentTool["renderCall"];
|
|
23
|
-
renderResult?: AgentTool["renderResult"];
|
|
24
|
+
parameters: TParameters;
|
|
25
|
+
renderCall?: AgentTool<TParameters, TDetails>["renderCall"];
|
|
26
|
+
renderResult?: AgentTool<TParameters, TDetails>["renderResult"];
|
|
24
27
|
|
|
25
28
|
constructor(
|
|
26
|
-
private tool: AgentTool<
|
|
29
|
+
private tool: AgentTool<TParameters, TDetails>,
|
|
27
30
|
private hookRunner: HookRunner,
|
|
28
31
|
) {
|
|
29
32
|
this.name = tool.name;
|
|
@@ -36,9 +39,9 @@ export class HookToolWrapper<T> implements AgentTool<any, T> {
|
|
|
36
39
|
|
|
37
40
|
async execute(
|
|
38
41
|
toolCallId: string,
|
|
39
|
-
params:
|
|
42
|
+
params: Static<TParameters>,
|
|
40
43
|
signal?: AbortSignal,
|
|
41
|
-
onUpdate?: AgentToolUpdateCallback<
|
|
44
|
+
onUpdate?: AgentToolUpdateCallback<TDetails, TParameters>,
|
|
42
45
|
context?: AgentToolContext,
|
|
43
46
|
) {
|
|
44
47
|
// Emit tool_call event - hooks can block execution
|
|
@@ -49,7 +52,7 @@ export class HookToolWrapper<T> implements AgentTool<any, T> {
|
|
|
49
52
|
type: "tool_call",
|
|
50
53
|
toolName: this.tool.name,
|
|
51
54
|
toolCallId,
|
|
52
|
-
input: params,
|
|
55
|
+
input: params as Record<string, unknown>,
|
|
53
56
|
})) as ToolCallEventResult | undefined;
|
|
54
57
|
|
|
55
58
|
if (callResult?.block) {
|
|
@@ -75,7 +78,7 @@ export class HookToolWrapper<T> implements AgentTool<any, T> {
|
|
|
75
78
|
type: "tool_result",
|
|
76
79
|
toolName: this.tool.name,
|
|
77
80
|
toolCallId,
|
|
78
|
-
input: params,
|
|
81
|
+
input: params as Record<string, unknown>,
|
|
79
82
|
content: result.content,
|
|
80
83
|
details: result.details,
|
|
81
84
|
isError: false,
|
|
@@ -85,7 +88,7 @@ export class HookToolWrapper<T> implements AgentTool<any, T> {
|
|
|
85
88
|
if (resultResult) {
|
|
86
89
|
return {
|
|
87
90
|
content: resultResult.content ?? result.content,
|
|
88
|
-
details: (resultResult.details ?? result.details) as
|
|
91
|
+
details: (resultResult.details ?? result.details) as TDetails,
|
|
89
92
|
};
|
|
90
93
|
}
|
|
91
94
|
}
|
|
@@ -98,7 +101,7 @@ export class HookToolWrapper<T> implements AgentTool<any, T> {
|
|
|
98
101
|
type: "tool_result",
|
|
99
102
|
toolName: this.tool.name,
|
|
100
103
|
toolCallId,
|
|
101
|
-
input: params,
|
|
104
|
+
input: params as Record<string, unknown>,
|
|
102
105
|
content: [{ type: "text", text: err instanceof Error ? err.message : String(err) }],
|
|
103
106
|
details: undefined,
|
|
104
107
|
isError: true,
|
|
@@ -108,18 +111,3 @@ export class HookToolWrapper<T> implements AgentTool<any, T> {
|
|
|
108
111
|
}
|
|
109
112
|
}
|
|
110
113
|
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Wrap all tools with hook callbacks.
|
|
114
|
-
*/
|
|
115
|
-
export function wrapToolsWithHooks<T>(tools: AgentTool<any, T>[], hookRunner: HookRunner): AgentTool<any, T>[] {
|
|
116
|
-
return tools.map((tool) => new HookToolWrapper(tool, hookRunner));
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Backward compatibility alias - use HookToolWrapper directly.
|
|
121
|
-
* @deprecated Use HookToolWrapper class instead
|
|
122
|
-
*/
|
|
123
|
-
export function wrapToolWithHooks<T>(tool: AgentTool<any, T>, hookRunner: HookRunner): AgentTool<any, T> {
|
|
124
|
-
return new HookToolWrapper(tool, hookRunner);
|
|
125
|
-
}
|
package/src/core/hooks/types.ts
CHANGED
|
@@ -11,7 +11,6 @@ import type { Component, TUI } from "@oh-my-pi/pi-tui";
|
|
|
11
11
|
import type { Theme } from "../../modes/interactive/theme/theme";
|
|
12
12
|
import type { CompactionPreparation, CompactionResult } from "../compaction/index";
|
|
13
13
|
import type { ExecOptions, ExecResult } from "../exec";
|
|
14
|
-
import type { Logger } from "../logger";
|
|
15
14
|
import type { HookMessage } from "../messages";
|
|
16
15
|
import type { ModelRegistry } from "../model-registry";
|
|
17
16
|
import type {
|
|
@@ -742,7 +741,7 @@ export interface HookAPI {
|
|
|
742
741
|
exec(command: string, args: string[], options?: ExecOptions): Promise<ExecResult>;
|
|
743
742
|
|
|
744
743
|
/** File logger for error/warning/debug messages */
|
|
745
|
-
logger:
|
|
744
|
+
logger: typeof import("@oh-my-pi/pi-utils").logger;
|
|
746
745
|
/** Injected @sinclair/typebox module */
|
|
747
746
|
typebox: typeof import("@sinclair/typebox");
|
|
748
747
|
/** Injected pi-coding-agent exports */
|
package/src/core/keybindings.ts
CHANGED
|
@@ -9,8 +9,8 @@ import {
|
|
|
9
9
|
matchesKey,
|
|
10
10
|
setEditorKeybindings,
|
|
11
11
|
} from "@oh-my-pi/pi-tui";
|
|
12
|
+
import { logger } from "@oh-my-pi/pi-utils";
|
|
12
13
|
import { getAgentDir } from "../config";
|
|
13
|
-
import { logger } from "./logger";
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Application-level actions (coding agent specific).
|
package/src/core/mcp/client.ts
CHANGED
|
@@ -36,19 +36,19 @@ const CLIENT_INFO = {
|
|
|
36
36
|
|
|
37
37
|
/** Wrap a promise with a timeout */
|
|
38
38
|
function withTimeout<T>(promise: Promise<T>, ms: number, message: string): Promise<T> {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
39
|
+
const { promise: wrapped, resolve, reject } = Promise.withResolvers<T>();
|
|
40
|
+
const timer = setTimeout(() => reject(new Error(message)), ms);
|
|
41
|
+
promise.then(
|
|
42
|
+
(value) => {
|
|
43
|
+
clearTimeout(timer);
|
|
44
|
+
resolve(value);
|
|
45
|
+
},
|
|
46
|
+
(error) => {
|
|
47
|
+
clearTimeout(timer);
|
|
48
|
+
reject(error);
|
|
49
|
+
},
|
|
50
|
+
);
|
|
51
|
+
return wrapped;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
/**
|
package/src/core/mcp/json-rpc.ts
CHANGED
package/src/core/mcp/loader.ts
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
* Integrates MCP tool discovery with the custom tools system.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import { logger } from "@oh-my-pi/pi-utils";
|
|
7
8
|
import { AgentStorage } from "../agent-storage";
|
|
8
9
|
import type { LoadedCustomTool } from "../custom-tools/types";
|
|
9
|
-
import { logger } from "../logger";
|
|
10
10
|
import { type MCPLoadResult, MCPManager } from "./manager";
|
|
11
11
|
import { parseMCPToolName } from "./tool-bridge";
|
|
12
12
|
import { MCPToolCache } from "./tool-cache";
|
package/src/core/mcp/manager.ts
CHANGED
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
* Handles tool loading and lifecycle.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import { logger } from "@oh-my-pi/pi-utils";
|
|
8
9
|
import type { TSchema } from "@sinclair/typebox";
|
|
9
10
|
import type { CustomTool } from "../custom-tools/types";
|
|
10
|
-
import { logger } from "../logger";
|
|
11
11
|
import { connectToServer, disconnectServer, listTools } from "./client";
|
|
12
12
|
import { loadAllMCPConfigs, validateServerConfig } from "./config";
|
|
13
13
|
import type { MCPToolDetails } from "./tool-bridge";
|
|
@@ -47,7 +47,7 @@ function trackPromise<T>(promise: Promise<T>): TrackedPromise<T> {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
function delay(ms: number): Promise<void> {
|
|
50
|
-
return
|
|
50
|
+
return Bun.sleep(ms);
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
/** Result of loading MCP tools */
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* Stores tool definitions per server in agent.db for fast startup.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import { logger } from "@oh-my-pi/pi-utils";
|
|
7
8
|
import type { AgentStorage } from "../agent-storage";
|
|
8
|
-
import { logger } from "../logger";
|
|
9
9
|
import type { MCPServerConfig, MCPToolDefinition } from "./types";
|
|
10
10
|
|
|
11
11
|
const CACHE_VERSION = 1;
|
|
@@ -12,19 +12,7 @@ function generateId(): string {
|
|
|
12
12
|
return Math.random().toString(36).slice(2) + Date.now().toString(36);
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
function parseSSELine(line: string): { event?: string; data?: string; id?: string } | null {
|
|
17
|
-
if (line.startsWith("data:")) {
|
|
18
|
-
return { data: line.slice(5).trim() };
|
|
19
|
-
}
|
|
20
|
-
if (line.startsWith("event:")) {
|
|
21
|
-
return { event: line.slice(6).trim() };
|
|
22
|
-
}
|
|
23
|
-
if (line.startsWith("id:")) {
|
|
24
|
-
return { id: line.slice(3).trim() };
|
|
25
|
-
}
|
|
26
|
-
return null;
|
|
27
|
-
}
|
|
15
|
+
import { readSseEvents } from "@oh-my-pi/pi-utils";
|
|
28
16
|
|
|
29
17
|
/**
|
|
30
18
|
* HTTP transport for MCP servers.
|
|
@@ -95,30 +83,17 @@ export class HttpTransport implements MCPTransport {
|
|
|
95
83
|
}
|
|
96
84
|
|
|
97
85
|
// Read SSE stream
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
buffer += decoder.decode(value, { stream: true });
|
|
107
|
-
const lines = buffer.split("\n");
|
|
108
|
-
buffer = lines.pop() ?? "";
|
|
109
|
-
|
|
110
|
-
for (const line of lines) {
|
|
111
|
-
const parsed = parseSSELine(line);
|
|
112
|
-
if (parsed?.data && parsed.data !== "[DONE]") {
|
|
113
|
-
try {
|
|
114
|
-
const message = JSON.parse(parsed.data);
|
|
115
|
-
if ("method" in message && !("id" in message)) {
|
|
116
|
-
this.onNotification?.(message.method, message.params);
|
|
117
|
-
}
|
|
118
|
-
} catch {
|
|
119
|
-
// Ignore parse errors
|
|
120
|
-
}
|
|
86
|
+
for await (const event of readSseEvents(response.body)) {
|
|
87
|
+
if (!this._connected) break;
|
|
88
|
+
const data = event.data?.trim();
|
|
89
|
+
if (!data || data === "[DONE]") continue;
|
|
90
|
+
try {
|
|
91
|
+
const message = JSON.parse(data);
|
|
92
|
+
if ("method" in message && !("id" in message)) {
|
|
93
|
+
this.onNotification?.(message.method, message.params);
|
|
121
94
|
}
|
|
95
|
+
} catch {
|
|
96
|
+
// Ignore parse errors
|
|
122
97
|
}
|
|
123
98
|
}
|
|
124
99
|
} catch (error) {
|
|
@@ -192,44 +167,31 @@ export class HttpTransport implements MCPTransport {
|
|
|
192
167
|
throw new Error("No response body");
|
|
193
168
|
}
|
|
194
169
|
|
|
195
|
-
const reader = response.body.getReader();
|
|
196
|
-
const decoder = new TextDecoder();
|
|
197
|
-
let buffer = "";
|
|
198
170
|
let result: T | undefined;
|
|
199
171
|
|
|
200
|
-
|
|
201
|
-
const
|
|
202
|
-
if (
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
if (parsed?.data && parsed.data !== "[DONE]") {
|
|
211
|
-
try {
|
|
212
|
-
const message = JSON.parse(parsed.data) as JsonRpcResponse;
|
|
213
|
-
|
|
214
|
-
// Handle our response
|
|
215
|
-
if ("id" in message && message.id === expectedId) {
|
|
216
|
-
if (message.error) {
|
|
217
|
-
throw new Error(`MCP error ${message.error.code}: ${message.error.message}`);
|
|
218
|
-
}
|
|
219
|
-
result = message.result as T;
|
|
220
|
-
}
|
|
221
|
-
// Handle notifications
|
|
222
|
-
else if ("method" in message && !("id" in message)) {
|
|
223
|
-
const notification = message as { method: string; params?: unknown };
|
|
224
|
-
this.onNotification?.(notification.method, notification.params);
|
|
225
|
-
}
|
|
226
|
-
} catch (error) {
|
|
227
|
-
if (error instanceof Error && error.message.startsWith("MCP error")) {
|
|
228
|
-
throw error;
|
|
229
|
-
}
|
|
230
|
-
// Ignore other parse errors
|
|
172
|
+
for await (const event of readSseEvents(response.body)) {
|
|
173
|
+
const data = event.data?.trim();
|
|
174
|
+
if (!data || data === "[DONE]") continue;
|
|
175
|
+
try {
|
|
176
|
+
const message = JSON.parse(data) as JsonRpcResponse;
|
|
177
|
+
|
|
178
|
+
// Handle our response
|
|
179
|
+
if ("id" in message && message.id === expectedId) {
|
|
180
|
+
if (message.error) {
|
|
181
|
+
throw new Error(`MCP error ${message.error.code}: ${message.error.message}`);
|
|
231
182
|
}
|
|
183
|
+
result = message.result as T;
|
|
184
|
+
}
|
|
185
|
+
// Handle notifications
|
|
186
|
+
else if ("method" in message && !("id" in message)) {
|
|
187
|
+
const notification = message as { method: string; params?: unknown };
|
|
188
|
+
this.onNotification?.(notification.method, notification.params);
|
|
189
|
+
}
|
|
190
|
+
} catch (error) {
|
|
191
|
+
if (error instanceof Error && error.message.startsWith("MCP error")) {
|
|
192
|
+
throw error;
|
|
232
193
|
}
|
|
194
|
+
// Ignore other parse errors
|
|
233
195
|
}
|
|
234
196
|
}
|
|
235
197
|
|
|
@@ -11,10 +11,10 @@ import {
|
|
|
11
11
|
type Model,
|
|
12
12
|
normalizeDomain,
|
|
13
13
|
} from "@oh-my-pi/pi-ai";
|
|
14
|
+
import { logger } from "@oh-my-pi/pi-utils";
|
|
14
15
|
import { type Static, Type } from "@sinclair/typebox";
|
|
15
16
|
import AjvModule from "ajv";
|
|
16
17
|
import type { AuthStorage } from "./auth-storage";
|
|
17
|
-
import { logger } from "./logger";
|
|
18
18
|
|
|
19
19
|
const Ajv = (AjvModule as any).default || AjvModule;
|
|
20
20
|
|
|
@@ -38,8 +38,9 @@ export async function installPlugin(packageName: string): Promise<InstalledPlugi
|
|
|
38
38
|
|
|
39
39
|
// Initialize package.json if it doesn't exist
|
|
40
40
|
const pkgJsonPath = join(PLUGINS_DIR, "package.json");
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
const pkgJson = Bun.file(pkgJsonPath);
|
|
42
|
+
if (!(await pkgJson.exists())) {
|
|
43
|
+
await pkgJson.write(JSON.stringify({ name: "omp-plugins", private: true, dependencies: {} }, null, 2));
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
// Run npm install in plugins directory
|
|
@@ -98,24 +99,25 @@ export async function uninstallPlugin(name: string): Promise<void> {
|
|
|
98
99
|
}
|
|
99
100
|
|
|
100
101
|
export async function listPlugins(): Promise<InstalledPlugin[]> {
|
|
101
|
-
const pkgJsonPath = join(PLUGINS_DIR, "package.json");
|
|
102
|
-
if (!(await
|
|
102
|
+
const pkgJsonPath = Bun.file(join(PLUGINS_DIR, "package.json"));
|
|
103
|
+
if (!(await pkgJsonPath.exists())) {
|
|
103
104
|
return [];
|
|
104
105
|
}
|
|
105
106
|
|
|
106
|
-
const pkg = await
|
|
107
|
+
const pkg = await pkgJsonPath.json();
|
|
107
108
|
const deps = pkg.dependencies || {};
|
|
108
109
|
|
|
109
110
|
const plugins: InstalledPlugin[] = [];
|
|
110
111
|
for (const [name, _version] of Object.entries(deps)) {
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
112
|
+
const path = join(PLUGINS_DIR, "node_modules", name);
|
|
113
|
+
const fpkg = Bun.file(join(path, "package.json"));
|
|
114
|
+
if (await fpkg.exists()) {
|
|
115
|
+
const pkg = await fpkg.json();
|
|
114
116
|
plugins.push({
|
|
115
117
|
name,
|
|
116
|
-
version:
|
|
117
|
-
path
|
|
118
|
-
manifest:
|
|
118
|
+
version: pkg.version,
|
|
119
|
+
path,
|
|
120
|
+
manifest: pkg.omp || pkg.pi || { version: pkg.version },
|
|
119
121
|
enabledFeatures: null,
|
|
120
122
|
enabled: true,
|
|
121
123
|
});
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { join, resolve } from "node:path";
|
|
2
|
+
import { logger } from "@oh-my-pi/pi-utils";
|
|
2
3
|
import Handlebars from "handlebars";
|
|
3
4
|
import { CONFIG_DIR_NAME, getPromptsDir } from "../config";
|
|
4
5
|
import { parseFrontmatter } from "./frontmatter";
|
|
5
|
-
import { logger } from "./logger";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Represents a prompt template loaded from a markdown file
|
|
@@ -371,14 +371,6 @@ async function loadTemplatesFromDir(
|
|
|
371
371
|
subdir: string = "",
|
|
372
372
|
): Promise<PromptTemplate[]> {
|
|
373
373
|
const templates: PromptTemplate[] = [];
|
|
374
|
-
|
|
375
|
-
try {
|
|
376
|
-
const stat = await Bun.file(`${dir}/.`).exists();
|
|
377
|
-
if (!stat) return templates;
|
|
378
|
-
} catch {
|
|
379
|
-
return templates;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
374
|
try {
|
|
383
375
|
const glob = new Bun.Glob("**/*");
|
|
384
376
|
const entries = [];
|
|
@@ -440,6 +432,9 @@ async function loadTemplatesFromDir(
|
|
|
440
432
|
}
|
|
441
433
|
}
|
|
442
434
|
} catch (error) {
|
|
435
|
+
if (!Bun.file(dir).exists()) {
|
|
436
|
+
return [];
|
|
437
|
+
}
|
|
443
438
|
logger.warn("Failed to scan prompt templates directory", { dir, error: String(error) });
|
|
444
439
|
}
|
|
445
440
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { logger } from "
|
|
1
|
+
import { logger, sanitizeText } from "@oh-my-pi/pi-utils";
|
|
2
2
|
import {
|
|
3
3
|
checkPythonKernelAvailability,
|
|
4
4
|
type KernelDisplayOutput,
|
|
@@ -7,9 +7,7 @@ import {
|
|
|
7
7
|
type PreludeHelper,
|
|
8
8
|
PythonKernel,
|
|
9
9
|
} from "./python-kernel";
|
|
10
|
-
import { OutputSink
|
|
11
|
-
import { DEFAULT_MAX_BYTES } from "./tools/truncate";
|
|
12
|
-
|
|
10
|
+
import { OutputSink } from "./streaming-output";
|
|
13
11
|
export type PythonKernelMode = "session" | "per-call";
|
|
14
12
|
|
|
15
13
|
export interface PythonExecutorOptions {
|
|
@@ -212,21 +210,30 @@ async function executeWithKernel(
|
|
|
212
210
|
code: string,
|
|
213
211
|
options: PythonExecutorOptions | undefined,
|
|
214
212
|
): Promise<PythonResult> {
|
|
215
|
-
const sink = new OutputSink(
|
|
216
|
-
const writer = sink.getWriter();
|
|
213
|
+
const sink = new OutputSink({ onLine: options?.onChunk });
|
|
217
214
|
const displayOutputs: KernelDisplayOutput[] = [];
|
|
218
215
|
|
|
219
216
|
try {
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
217
|
+
const writable = sink.createStringWritable();
|
|
218
|
+
const writer = writable.getWriter();
|
|
219
|
+
let result: KernelExecuteResult;
|
|
220
|
+
try {
|
|
221
|
+
result = await kernel.execute(code, {
|
|
222
|
+
signal: options?.signal,
|
|
223
|
+
timeoutMs: options?.timeout,
|
|
224
|
+
onChunk: (text) => {
|
|
225
|
+
writer.write(sanitizeText(text));
|
|
226
|
+
},
|
|
227
|
+
onDisplay: (output) => {
|
|
228
|
+
displayOutputs.push(output);
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
} catch (err) {
|
|
232
|
+
await writer.abort(err);
|
|
233
|
+
throw err;
|
|
234
|
+
} finally {
|
|
235
|
+
await writer.close().catch(() => {});
|
|
236
|
+
}
|
|
230
237
|
|
|
231
238
|
if (result.cancelled) {
|
|
232
239
|
const secs = options?.timeout ? Math.round(options.timeout / 1000) : undefined;
|
|
@@ -263,8 +270,6 @@ async function executeWithKernel(
|
|
|
263
270
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
264
271
|
logger.error("Python execution failed", { error: error.message });
|
|
265
272
|
throw error;
|
|
266
|
-
} finally {
|
|
267
|
-
await writer.close();
|
|
268
273
|
}
|
|
269
274
|
}
|
|
270
275
|
|
|
@@ -13,11 +13,11 @@ import {
|
|
|
13
13
|
} from "node:fs";
|
|
14
14
|
import { createServer } from "node:net";
|
|
15
15
|
import { delimiter, join } from "node:path";
|
|
16
|
+
import { logger } from "@oh-my-pi/pi-utils";
|
|
16
17
|
import type { Subprocess } from "bun";
|
|
17
18
|
import { getAgentDir } from "../config";
|
|
18
19
|
import { getShellConfig, killProcessTree } from "../utils/shell";
|
|
19
20
|
import { getOrCreateSnapshot } from "../utils/shell-snapshot";
|
|
20
|
-
import { logger } from "./logger";
|
|
21
21
|
|
|
22
22
|
const GATEWAY_DIR_NAME = "python-gateway";
|
|
23
23
|
const GATEWAY_INFO_FILE = "gateway.json";
|
|
@@ -213,27 +213,28 @@ async function resolvePythonRuntime(cwd: string, baseEnv: Record<string, string
|
|
|
213
213
|
}
|
|
214
214
|
|
|
215
215
|
async function allocatePort(): Promise<number> {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
});
|
|
216
|
+
const { promise, resolve, reject } = Promise.withResolvers<number>();
|
|
217
|
+
const server = createServer();
|
|
218
|
+
server.unref();
|
|
219
|
+
server.on("error", reject);
|
|
220
|
+
server.listen(0, "127.0.0.1", () => {
|
|
221
|
+
const address = server.address();
|
|
222
|
+
if (address && typeof address === "object") {
|
|
223
|
+
const port = address.port;
|
|
224
|
+
server.close((err: Error | null | undefined) => {
|
|
225
|
+
if (err) {
|
|
226
|
+
reject(err);
|
|
227
|
+
} else {
|
|
228
|
+
resolve(port);
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
} else {
|
|
232
|
+
server.close();
|
|
233
|
+
reject(new Error("Failed to allocate port"));
|
|
234
|
+
}
|
|
236
235
|
});
|
|
236
|
+
|
|
237
|
+
return promise;
|
|
237
238
|
}
|
|
238
239
|
|
|
239
240
|
function getGatewayDir(): string {
|
|
@@ -587,7 +588,7 @@ async function startGatewayProcess(
|
|
|
587
588
|
await Bun.sleep(100);
|
|
588
589
|
}
|
|
589
590
|
|
|
590
|
-
killProcessTree(gatewayProcess.pid);
|
|
591
|
+
await killProcessTree(gatewayProcess.pid);
|
|
591
592
|
throw new Error("Gateway startup timeout");
|
|
592
593
|
}
|
|
593
594
|
|
|
@@ -613,10 +614,10 @@ function scheduleIdleShutdown(): void {
|
|
|
613
614
|
}
|
|
614
615
|
logger.debug("Shutting down idle shared gateway", { pid: info.pid });
|
|
615
616
|
if (localGatewayProcess) {
|
|
616
|
-
shutdownLocalGateway();
|
|
617
|
+
await shutdownLocalGateway();
|
|
617
618
|
} else if (isPidRunning(info.pid)) {
|
|
618
619
|
try {
|
|
619
|
-
killProcessTree(info.pid);
|
|
620
|
+
await killProcessTree(info.pid);
|
|
620
621
|
} catch (err) {
|
|
621
622
|
logger.warn("Failed to kill idle shared gateway", {
|
|
622
623
|
error: err instanceof Error ? err.message : String(err),
|
|
@@ -644,10 +645,10 @@ function cancelIdleShutdown(): void {
|
|
|
644
645
|
}
|
|
645
646
|
}
|
|
646
647
|
|
|
647
|
-
function shutdownLocalGateway(): void {
|
|
648
|
+
async function shutdownLocalGateway(): Promise<void> {
|
|
648
649
|
if (localGatewayProcess) {
|
|
649
650
|
try {
|
|
650
|
-
killProcessTree(localGatewayProcess.pid);
|
|
651
|
+
await killProcessTree(localGatewayProcess.pid);
|
|
651
652
|
} catch (err) {
|
|
652
653
|
logger.warn("Failed to kill shared gateway process", {
|
|
653
654
|
error: err instanceof Error ? err.message : String(err),
|
|
@@ -701,7 +702,7 @@ export async function acquireSharedGateway(cwd: string): Promise<AcquireResult |
|
|
|
701
702
|
logger.debug("Cleaning up stale gateway info", { pid: existingInfo.pid });
|
|
702
703
|
if (isPidRunning(existingInfo.pid)) {
|
|
703
704
|
try {
|
|
704
|
-
killProcessTree(existingInfo.pid);
|
|
705
|
+
await killProcessTree(existingInfo.pid);
|
|
705
706
|
} catch (err) {
|
|
706
707
|
logger.warn("Failed to kill stale shared gateway process", {
|
|
707
708
|
error: err instanceof Error ? err.message : String(err),
|
|
@@ -826,7 +827,7 @@ export async function shutdownSharedGateway(): Promise<void> {
|
|
|
826
827
|
error: err instanceof Error ? err.message : String(err),
|
|
827
828
|
});
|
|
828
829
|
} finally {
|
|
829
|
-
shutdownLocalGateway();
|
|
830
|
+
await shutdownLocalGateway();
|
|
830
831
|
isCoordinatorInitialized = false;
|
|
831
832
|
}
|
|
832
833
|
}
|