@oh-my-pi/pi-coding-agent 3.37.1 → 4.0.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/CHANGELOG.md +103 -0
- package/README.md +44 -3
- package/docs/extensions.md +29 -4
- package/docs/sdk.md +3 -3
- package/package.json +5 -5
- package/src/cli/args.ts +8 -0
- package/src/config.ts +5 -15
- package/src/core/agent-session.ts +217 -51
- package/src/core/auth-storage.ts +456 -47
- package/src/core/bash-executor.ts +79 -14
- package/src/core/custom-commands/types.ts +1 -1
- package/src/core/custom-tools/types.ts +1 -1
- package/src/core/export-html/index.ts +33 -1
- package/src/core/export-html/template.css +99 -0
- package/src/core/export-html/template.generated.ts +1 -1
- package/src/core/export-html/template.js +133 -8
- package/src/core/extensions/index.ts +22 -4
- package/src/core/extensions/loader.ts +152 -214
- package/src/core/extensions/runner.ts +139 -79
- package/src/core/extensions/types.ts +143 -19
- package/src/core/extensions/wrapper.ts +5 -8
- package/src/core/hooks/types.ts +1 -1
- package/src/core/index.ts +2 -1
- package/src/core/keybindings.ts +4 -1
- package/src/core/model-registry.ts +4 -4
- package/src/core/model-resolver.ts +35 -26
- package/src/core/sdk.ts +96 -76
- package/src/core/settings-manager.ts +45 -14
- package/src/core/system-prompt.ts +5 -15
- package/src/core/tools/bash.ts +115 -54
- package/src/core/tools/find.ts +86 -7
- package/src/core/tools/grep.ts +27 -6
- package/src/core/tools/index.ts +15 -6
- package/src/core/tools/ls.ts +49 -18
- package/src/core/tools/render-utils.ts +2 -1
- package/src/core/tools/task/worker.ts +35 -12
- package/src/core/tools/web-search/auth.ts +37 -32
- package/src/core/tools/web-search/providers/anthropic.ts +35 -22
- package/src/index.ts +101 -9
- package/src/main.ts +60 -20
- package/src/migrations.ts +47 -2
- package/src/modes/index.ts +2 -2
- package/src/modes/interactive/components/assistant-message.ts +25 -7
- package/src/modes/interactive/components/bash-execution.ts +5 -0
- package/src/modes/interactive/components/branch-summary-message.ts +5 -0
- package/src/modes/interactive/components/compaction-summary-message.ts +5 -0
- package/src/modes/interactive/components/countdown-timer.ts +38 -0
- package/src/modes/interactive/components/custom-editor.ts +8 -0
- package/src/modes/interactive/components/custom-message.ts +5 -0
- package/src/modes/interactive/components/footer.ts +2 -5
- package/src/modes/interactive/components/hook-input.ts +29 -20
- package/src/modes/interactive/components/hook-selector.ts +52 -38
- package/src/modes/interactive/components/index.ts +39 -0
- package/src/modes/interactive/components/login-dialog.ts +160 -0
- package/src/modes/interactive/components/model-selector.ts +10 -2
- package/src/modes/interactive/components/session-selector.ts +5 -1
- package/src/modes/interactive/components/settings-defs.ts +9 -0
- package/src/modes/interactive/components/status-line/segments.ts +3 -3
- package/src/modes/interactive/components/tool-execution.ts +9 -16
- package/src/modes/interactive/components/tree-selector.ts +1 -6
- package/src/modes/interactive/interactive-mode.ts +466 -215
- package/src/modes/interactive/theme/theme.ts +50 -2
- package/src/modes/print-mode.ts +78 -31
- package/src/modes/rpc/rpc-mode.ts +186 -78
- package/src/modes/rpc/rpc-types.ts +10 -3
- package/src/prompts/system-prompt.md +36 -28
- package/src/utils/clipboard.ts +90 -50
- package/src/utils/image-convert.ts +1 -1
- package/src/utils/image-resize.ts +1 -1
- package/src/utils/tools-manager.ts +2 -2
|
@@ -2,8 +2,14 @@
|
|
|
2
2
|
* Extension system for lifecycle events and custom tools.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
export { discoverAndLoadExtensions, loadExtensionFromFactory, loadExtensions } from "./loader";
|
|
6
|
-
export type {
|
|
5
|
+
export { createExtensionRuntime, discoverAndLoadExtensions, loadExtensionFromFactory, loadExtensions } from "./loader";
|
|
6
|
+
export type {
|
|
7
|
+
BranchHandler,
|
|
8
|
+
ExtensionErrorListener,
|
|
9
|
+
NavigateTreeHandler,
|
|
10
|
+
NewSessionHandler,
|
|
11
|
+
ShutdownHandler,
|
|
12
|
+
} from "./runner";
|
|
7
13
|
export { ExtensionRunner } from "./runner";
|
|
8
14
|
export type {
|
|
9
15
|
AgentEndEvent,
|
|
@@ -11,6 +17,7 @@ export type {
|
|
|
11
17
|
// Re-exports
|
|
12
18
|
AgentToolResult,
|
|
13
19
|
AgentToolUpdateCallback,
|
|
20
|
+
AppAction,
|
|
14
21
|
AppendEntryHandler,
|
|
15
22
|
BashToolResultEvent,
|
|
16
23
|
BeforeAgentStartEvent,
|
|
@@ -23,26 +30,32 @@ export type {
|
|
|
23
30
|
EditToolResultEvent,
|
|
24
31
|
ExecOptions,
|
|
25
32
|
ExecResult,
|
|
33
|
+
Extension,
|
|
34
|
+
ExtensionActions,
|
|
26
35
|
// API
|
|
27
36
|
ExtensionAPI,
|
|
28
37
|
ExtensionCommandContext,
|
|
38
|
+
ExtensionCommandContextActions,
|
|
29
39
|
// Context
|
|
30
40
|
ExtensionContext,
|
|
41
|
+
ExtensionContextActions,
|
|
31
42
|
// Errors
|
|
32
43
|
ExtensionError,
|
|
33
44
|
ExtensionEvent,
|
|
34
45
|
ExtensionFactory,
|
|
35
46
|
ExtensionFlag,
|
|
36
47
|
ExtensionHandler,
|
|
48
|
+
ExtensionRuntime,
|
|
37
49
|
ExtensionShortcut,
|
|
38
50
|
ExtensionUIContext,
|
|
51
|
+
ExtensionUIDialogOptions,
|
|
39
52
|
FindToolResultEvent,
|
|
40
53
|
GetActiveToolsHandler,
|
|
41
54
|
GetAllToolsHandler,
|
|
55
|
+
GetThinkingLevelHandler,
|
|
42
56
|
GrepToolResultEvent,
|
|
57
|
+
KeybindingsManager,
|
|
43
58
|
LoadExtensionsResult,
|
|
44
|
-
// Loaded Extension
|
|
45
|
-
LoadedExtension,
|
|
46
59
|
LsToolResultEvent,
|
|
47
60
|
// Message Rendering
|
|
48
61
|
MessageRenderer,
|
|
@@ -52,6 +65,7 @@ export type {
|
|
|
52
65
|
RegisteredCommand,
|
|
53
66
|
RegisteredTool,
|
|
54
67
|
SendMessageHandler,
|
|
68
|
+
SendUserMessageHandler,
|
|
55
69
|
SessionBeforeBranchEvent,
|
|
56
70
|
SessionBeforeBranchResult,
|
|
57
71
|
SessionBeforeCompactEvent,
|
|
@@ -69,6 +83,8 @@ export type {
|
|
|
69
83
|
SessionSwitchEvent,
|
|
70
84
|
SessionTreeEvent,
|
|
71
85
|
SetActiveToolsHandler,
|
|
86
|
+
SetModelHandler,
|
|
87
|
+
SetThinkingLevelHandler,
|
|
72
88
|
// Events - Tool
|
|
73
89
|
ToolCallEvent,
|
|
74
90
|
ToolCallEventResult,
|
|
@@ -80,6 +96,8 @@ export type {
|
|
|
80
96
|
TreePreparation,
|
|
81
97
|
TurnEndEvent,
|
|
82
98
|
TurnStartEvent,
|
|
99
|
+
UserBashEvent,
|
|
100
|
+
UserBashEventResult,
|
|
83
101
|
WriteToolResultEvent,
|
|
84
102
|
} from "./types";
|
|
85
103
|
// Type guards
|
|
@@ -11,28 +11,19 @@ import { type ExtensionModule, extensionModuleCapability } from "../../capabilit
|
|
|
11
11
|
import { loadSync } from "../../discovery";
|
|
12
12
|
import { getExtensionNameFromPath } from "../../discovery/helpers";
|
|
13
13
|
import * as piCodingAgent from "../../index";
|
|
14
|
-
import { theme } from "../../modes/interactive/theme/theme";
|
|
15
14
|
import { createEventBus, type EventBus } from "../event-bus";
|
|
16
15
|
import type { ExecOptions } from "../exec";
|
|
17
16
|
import { execCommand } from "../exec";
|
|
18
17
|
import { logger } from "../logger";
|
|
19
18
|
import type {
|
|
20
|
-
|
|
19
|
+
Extension,
|
|
21
20
|
ExtensionAPI,
|
|
22
21
|
ExtensionContext,
|
|
23
22
|
ExtensionFactory,
|
|
24
|
-
|
|
25
|
-
ExtensionShortcut,
|
|
26
|
-
ExtensionUIContext,
|
|
27
|
-
GetActiveToolsHandler,
|
|
28
|
-
GetAllToolsHandler,
|
|
23
|
+
ExtensionRuntime,
|
|
29
24
|
LoadExtensionsResult,
|
|
30
|
-
LoadedExtension,
|
|
31
25
|
MessageRenderer,
|
|
32
26
|
RegisteredCommand,
|
|
33
|
-
RegisteredTool,
|
|
34
|
-
SendMessageHandler,
|
|
35
|
-
SetActiveToolsHandler,
|
|
36
27
|
ToolDefinition,
|
|
37
28
|
} from "./types";
|
|
38
29
|
|
|
@@ -61,80 +52,62 @@ function resolvePath(extPath: string, cwd: string): string {
|
|
|
61
52
|
return path.resolve(cwd, expanded);
|
|
62
53
|
}
|
|
63
54
|
|
|
64
|
-
|
|
55
|
+
type HandlerFn = (...args: unknown[]) => Promise<unknown>;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Create a runtime with throwing stubs for action methods.
|
|
59
|
+
* Runner.initialize() replaces these with real implementations.
|
|
60
|
+
*/
|
|
61
|
+
export function createExtensionRuntime(): ExtensionRuntime {
|
|
62
|
+
const notInitialized = () => {
|
|
63
|
+
throw new Error("Extension runtime not initialized. Action methods cannot be called during extension loading.");
|
|
64
|
+
};
|
|
65
|
+
|
|
65
66
|
return {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
editor: async () => undefined,
|
|
77
|
-
get theme() {
|
|
78
|
-
return theme;
|
|
79
|
-
},
|
|
67
|
+
sendMessage: notInitialized,
|
|
68
|
+
sendUserMessage: notInitialized,
|
|
69
|
+
appendEntry: notInitialized,
|
|
70
|
+
getActiveTools: notInitialized,
|
|
71
|
+
getAllTools: notInitialized,
|
|
72
|
+
setActiveTools: notInitialized,
|
|
73
|
+
setModel: () => Promise.reject(new Error("Extension runtime not initialized")),
|
|
74
|
+
getThinkingLevel: notInitialized,
|
|
75
|
+
setThinkingLevel: notInitialized,
|
|
76
|
+
flagValues: new Map(),
|
|
80
77
|
};
|
|
81
78
|
}
|
|
82
79
|
|
|
83
|
-
|
|
84
|
-
|
|
80
|
+
/**
|
|
81
|
+
* Create the ExtensionAPI for an extension.
|
|
82
|
+
* Registration methods write to the extension object.
|
|
83
|
+
* Action methods delegate to the shared runtime.
|
|
84
|
+
*/
|
|
85
85
|
function createExtensionAPI(
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
extension: Extension,
|
|
87
|
+
runtime: ExtensionRuntime,
|
|
88
88
|
cwd: string,
|
|
89
|
-
extensionPath: string,
|
|
90
89
|
eventBus: EventBus,
|
|
91
|
-
|
|
92
|
-
): {
|
|
93
|
-
api: ExtensionAPI;
|
|
94
|
-
messageRenderers: Map<string, MessageRenderer>;
|
|
95
|
-
commands: Map<string, RegisteredCommand>;
|
|
96
|
-
flags: Map<string, ExtensionFlag>;
|
|
97
|
-
flagValues: Map<string, boolean | string>;
|
|
98
|
-
shortcuts: Map<KeyId, ExtensionShortcut>;
|
|
99
|
-
setSendMessageHandler: (handler: SendMessageHandler) => void;
|
|
100
|
-
setAppendEntryHandler: (handler: AppendEntryHandler) => void;
|
|
101
|
-
setGetActiveToolsHandler: (handler: GetActiveToolsHandler) => void;
|
|
102
|
-
setGetAllToolsHandler: (handler: GetAllToolsHandler) => void;
|
|
103
|
-
setSetActiveToolsHandler: (handler: SetActiveToolsHandler) => void;
|
|
104
|
-
setFlagValue: (name: string, value: boolean | string) => void;
|
|
105
|
-
} {
|
|
106
|
-
let sendMessageHandler: SendMessageHandler = () => {};
|
|
107
|
-
let appendEntryHandler: AppendEntryHandler = () => {};
|
|
108
|
-
let getActiveToolsHandler: GetActiveToolsHandler = () => [];
|
|
109
|
-
let getAllToolsHandler: GetAllToolsHandler = () => [];
|
|
110
|
-
let setActiveToolsHandler: SetActiveToolsHandler = () => {};
|
|
111
|
-
|
|
112
|
-
const messageRenderers = new Map<string, MessageRenderer>();
|
|
113
|
-
const commands = new Map<string, RegisteredCommand>();
|
|
114
|
-
const flags = new Map<string, ExtensionFlag>();
|
|
115
|
-
const flagValues = new Map<string, boolean | string>();
|
|
116
|
-
const shortcuts = new Map<KeyId, ExtensionShortcut>();
|
|
117
|
-
|
|
90
|
+
): ExtensionAPI {
|
|
118
91
|
const api = {
|
|
119
92
|
logger,
|
|
120
93
|
typebox: TypeBox,
|
|
121
94
|
pi: piCodingAgent,
|
|
122
95
|
|
|
123
96
|
on(event: string, handler: HandlerFn): void {
|
|
124
|
-
const list = handlers.get(event) ?? [];
|
|
97
|
+
const list = extension.handlers.get(event) ?? [];
|
|
125
98
|
list.push(handler);
|
|
126
|
-
handlers.set(event, list);
|
|
99
|
+
extension.handlers.set(event, list);
|
|
127
100
|
},
|
|
128
101
|
|
|
129
102
|
registerTool(tool: ToolDefinition): void {
|
|
130
|
-
tools.set(tool.name, {
|
|
103
|
+
extension.tools.set(tool.name, {
|
|
131
104
|
definition: tool,
|
|
132
|
-
extensionPath,
|
|
105
|
+
extensionPath: extension.path,
|
|
133
106
|
});
|
|
134
107
|
},
|
|
135
108
|
|
|
136
109
|
registerCommand(name: string, options: { description?: string; handler: RegisteredCommand["handler"] }): void {
|
|
137
|
-
commands.set(name, { name, ...options });
|
|
110
|
+
extension.commands.set(name, { name, ...options });
|
|
138
111
|
},
|
|
139
112
|
|
|
140
113
|
registerShortcut(
|
|
@@ -144,33 +117,38 @@ function createExtensionAPI(
|
|
|
144
117
|
handler: (ctx: ExtensionContext) => Promise<void> | void;
|
|
145
118
|
},
|
|
146
119
|
): void {
|
|
147
|
-
shortcuts.set(shortcut, { shortcut, extensionPath, ...options });
|
|
120
|
+
extension.shortcuts.set(shortcut, { shortcut, extensionPath: extension.path, ...options });
|
|
148
121
|
},
|
|
149
122
|
|
|
150
123
|
registerFlag(
|
|
151
124
|
name: string,
|
|
152
125
|
options: { description?: string; type: "boolean" | "string"; default?: boolean | string },
|
|
153
126
|
): void {
|
|
154
|
-
flags.set(name, { name, extensionPath, ...options });
|
|
127
|
+
extension.flags.set(name, { name, extensionPath: extension.path, ...options });
|
|
155
128
|
if (options.default !== undefined) {
|
|
156
|
-
flagValues.set(name, options.default);
|
|
129
|
+
runtime.flagValues.set(name, options.default);
|
|
157
130
|
}
|
|
158
131
|
},
|
|
159
132
|
|
|
160
|
-
|
|
161
|
-
|
|
133
|
+
registerMessageRenderer<T>(customType: string, renderer: MessageRenderer<T>): void {
|
|
134
|
+
extension.messageRenderers.set(customType, renderer as MessageRenderer);
|
|
162
135
|
},
|
|
163
136
|
|
|
164
|
-
|
|
165
|
-
|
|
137
|
+
getFlag(name: string): boolean | string | undefined {
|
|
138
|
+
if (!extension.flags.has(name)) return undefined;
|
|
139
|
+
return runtime.flagValues.get(name);
|
|
166
140
|
},
|
|
167
141
|
|
|
168
142
|
sendMessage(message, options): void {
|
|
169
|
-
|
|
143
|
+
runtime.sendMessage(message, options);
|
|
144
|
+
},
|
|
145
|
+
|
|
146
|
+
sendUserMessage(content, options): void {
|
|
147
|
+
runtime.sendUserMessage(content, options);
|
|
170
148
|
},
|
|
171
149
|
|
|
172
150
|
appendEntry(customType: string, data?: unknown): void {
|
|
173
|
-
|
|
151
|
+
runtime.appendEntry(customType, data);
|
|
174
152
|
},
|
|
175
153
|
|
|
176
154
|
exec(command: string, args: string[], options?: ExecOptions) {
|
|
@@ -178,45 +156,48 @@ function createExtensionAPI(
|
|
|
178
156
|
},
|
|
179
157
|
|
|
180
158
|
getActiveTools(): string[] {
|
|
181
|
-
return
|
|
159
|
+
return runtime.getActiveTools();
|
|
182
160
|
},
|
|
183
161
|
|
|
184
162
|
getAllTools(): string[] {
|
|
185
|
-
return
|
|
163
|
+
return runtime.getAllTools();
|
|
186
164
|
},
|
|
187
165
|
|
|
188
166
|
setActiveTools(toolNames: string[]): void {
|
|
189
|
-
|
|
167
|
+
runtime.setActiveTools(toolNames);
|
|
168
|
+
},
|
|
169
|
+
|
|
170
|
+
setModel(model) {
|
|
171
|
+
return runtime.setModel(model);
|
|
172
|
+
},
|
|
173
|
+
|
|
174
|
+
getThinkingLevel() {
|
|
175
|
+
return runtime.getThinkingLevel();
|
|
176
|
+
},
|
|
177
|
+
|
|
178
|
+
setThinkingLevel(level) {
|
|
179
|
+
runtime.setThinkingLevel(level);
|
|
190
180
|
},
|
|
191
181
|
|
|
192
182
|
events: eventBus,
|
|
193
183
|
} as ExtensionAPI;
|
|
194
184
|
|
|
185
|
+
return api;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Create an Extension object with empty collections.
|
|
190
|
+
*/
|
|
191
|
+
function createExtension(extensionPath: string, resolvedPath: string): Extension {
|
|
195
192
|
return {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
},
|
|
205
|
-
setAppendEntryHandler: (handler: AppendEntryHandler) => {
|
|
206
|
-
appendEntryHandler = handler;
|
|
207
|
-
},
|
|
208
|
-
setGetActiveToolsHandler: (handler: GetActiveToolsHandler) => {
|
|
209
|
-
getActiveToolsHandler = handler;
|
|
210
|
-
},
|
|
211
|
-
setGetAllToolsHandler: (handler: GetAllToolsHandler) => {
|
|
212
|
-
getAllToolsHandler = handler;
|
|
213
|
-
},
|
|
214
|
-
setSetActiveToolsHandler: (handler: SetActiveToolsHandler) => {
|
|
215
|
-
setActiveToolsHandler = handler;
|
|
216
|
-
},
|
|
217
|
-
setFlagValue: (name: string, value: boolean | string) => {
|
|
218
|
-
flagValues.set(name, value);
|
|
219
|
-
},
|
|
193
|
+
path: extensionPath,
|
|
194
|
+
resolvedPath,
|
|
195
|
+
handlers: new Map(),
|
|
196
|
+
tools: new Map(),
|
|
197
|
+
messageRenderers: new Map(),
|
|
198
|
+
commands: new Map(),
|
|
199
|
+
flags: new Map(),
|
|
200
|
+
shortcuts: new Map(),
|
|
220
201
|
};
|
|
221
202
|
}
|
|
222
203
|
|
|
@@ -224,8 +205,8 @@ async function loadExtension(
|
|
|
224
205
|
extensionPath: string,
|
|
225
206
|
cwd: string,
|
|
226
207
|
eventBus: EventBus,
|
|
227
|
-
|
|
228
|
-
): Promise<{ extension:
|
|
208
|
+
runtime: ExtensionRuntime,
|
|
209
|
+
): Promise<{ extension: Extension | null; error: string | null }> {
|
|
229
210
|
const resolvedPath = resolvePath(extensionPath, cwd);
|
|
230
211
|
|
|
231
212
|
try {
|
|
@@ -233,48 +214,17 @@ async function loadExtension(
|
|
|
233
214
|
const factory = (module.default ?? module) as ExtensionFactory;
|
|
234
215
|
|
|
235
216
|
if (typeof factory !== "function") {
|
|
236
|
-
return {
|
|
217
|
+
return {
|
|
218
|
+
extension: null,
|
|
219
|
+
error: `Extension does not export a valid factory function: ${extensionPath}`,
|
|
220
|
+
};
|
|
237
221
|
}
|
|
238
222
|
|
|
239
|
-
const
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
commands,
|
|
245
|
-
flags,
|
|
246
|
-
flagValues,
|
|
247
|
-
shortcuts,
|
|
248
|
-
setSendMessageHandler,
|
|
249
|
-
setAppendEntryHandler,
|
|
250
|
-
setGetActiveToolsHandler,
|
|
251
|
-
setGetAllToolsHandler,
|
|
252
|
-
setSetActiveToolsHandler,
|
|
253
|
-
setFlagValue,
|
|
254
|
-
} = createExtensionAPI(handlers, tools, cwd, extensionPath, eventBus, sharedUI);
|
|
255
|
-
|
|
256
|
-
factory(api);
|
|
257
|
-
|
|
258
|
-
return {
|
|
259
|
-
extension: {
|
|
260
|
-
path: extensionPath,
|
|
261
|
-
resolvedPath,
|
|
262
|
-
handlers,
|
|
263
|
-
tools,
|
|
264
|
-
messageRenderers,
|
|
265
|
-
commands,
|
|
266
|
-
flags,
|
|
267
|
-
flagValues,
|
|
268
|
-
shortcuts,
|
|
269
|
-
setSendMessageHandler,
|
|
270
|
-
setAppendEntryHandler,
|
|
271
|
-
setGetActiveToolsHandler,
|
|
272
|
-
setGetAllToolsHandler,
|
|
273
|
-
setSetActiveToolsHandler,
|
|
274
|
-
setFlagValue,
|
|
275
|
-
},
|
|
276
|
-
error: null,
|
|
277
|
-
};
|
|
223
|
+
const extension = createExtension(extensionPath, resolvedPath);
|
|
224
|
+
const api = createExtensionAPI(extension, runtime, cwd, eventBus);
|
|
225
|
+
await factory(api);
|
|
226
|
+
|
|
227
|
+
return { extension, error: null };
|
|
278
228
|
} catch (err) {
|
|
279
229
|
const message = err instanceof Error ? err.message : String(err);
|
|
280
230
|
return { extension: null, error: `Failed to load extension: ${message}` };
|
|
@@ -282,64 +232,32 @@ async function loadExtension(
|
|
|
282
232
|
}
|
|
283
233
|
|
|
284
234
|
/**
|
|
285
|
-
* Create
|
|
235
|
+
* Create an Extension from an inline factory function.
|
|
286
236
|
*/
|
|
287
|
-
export function loadExtensionFromFactory(
|
|
237
|
+
export async function loadExtensionFromFactory(
|
|
288
238
|
factory: ExtensionFactory,
|
|
289
239
|
cwd: string,
|
|
290
240
|
eventBus: EventBus,
|
|
291
|
-
|
|
241
|
+
runtime: ExtensionRuntime,
|
|
292
242
|
name = "<inline>",
|
|
293
|
-
):
|
|
294
|
-
const
|
|
295
|
-
const
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
messageRenderers,
|
|
299
|
-
commands,
|
|
300
|
-
flags,
|
|
301
|
-
flagValues,
|
|
302
|
-
shortcuts,
|
|
303
|
-
setSendMessageHandler,
|
|
304
|
-
setAppendEntryHandler,
|
|
305
|
-
setGetActiveToolsHandler,
|
|
306
|
-
setGetAllToolsHandler,
|
|
307
|
-
setSetActiveToolsHandler,
|
|
308
|
-
setFlagValue,
|
|
309
|
-
} = createExtensionAPI(handlers, tools, cwd, name, eventBus, sharedUI);
|
|
310
|
-
|
|
311
|
-
factory(api);
|
|
312
|
-
|
|
313
|
-
return {
|
|
314
|
-
path: name,
|
|
315
|
-
resolvedPath: name,
|
|
316
|
-
handlers,
|
|
317
|
-
tools,
|
|
318
|
-
messageRenderers,
|
|
319
|
-
commands,
|
|
320
|
-
flags,
|
|
321
|
-
flagValues,
|
|
322
|
-
shortcuts,
|
|
323
|
-
setSendMessageHandler,
|
|
324
|
-
setAppendEntryHandler,
|
|
325
|
-
setGetActiveToolsHandler,
|
|
326
|
-
setGetAllToolsHandler,
|
|
327
|
-
setSetActiveToolsHandler,
|
|
328
|
-
setFlagValue,
|
|
329
|
-
};
|
|
243
|
+
): Promise<Extension> {
|
|
244
|
+
const extension = createExtension(name, name);
|
|
245
|
+
const api = createExtensionAPI(extension, runtime, cwd, eventBus);
|
|
246
|
+
await factory(api);
|
|
247
|
+
return extension;
|
|
330
248
|
}
|
|
331
249
|
|
|
332
250
|
/**
|
|
333
251
|
* Load extensions from paths.
|
|
334
252
|
*/
|
|
335
253
|
export async function loadExtensions(paths: string[], cwd: string, eventBus?: EventBus): Promise<LoadExtensionsResult> {
|
|
336
|
-
const extensions:
|
|
254
|
+
const extensions: Extension[] = [];
|
|
337
255
|
const errors: Array<{ path: string; error: string }> = [];
|
|
338
256
|
const resolvedEventBus = eventBus ?? createEventBus();
|
|
339
|
-
const
|
|
257
|
+
const runtime = createExtensionRuntime();
|
|
340
258
|
|
|
341
259
|
for (const extPath of paths) {
|
|
342
|
-
const { extension, error } = await loadExtension(extPath, cwd, resolvedEventBus,
|
|
260
|
+
const { extension, error } = await loadExtension(extPath, cwd, resolvedEventBus, runtime);
|
|
343
261
|
|
|
344
262
|
if (error) {
|
|
345
263
|
errors.push({ path: extPath, error });
|
|
@@ -354,10 +272,7 @@ export async function loadExtensions(paths: string[], cwd: string, eventBus?: Ev
|
|
|
354
272
|
return {
|
|
355
273
|
extensions,
|
|
356
274
|
errors,
|
|
357
|
-
|
|
358
|
-
sharedUI.ui = uiContext;
|
|
359
|
-
sharedUI.hasUI = hasUI;
|
|
360
|
-
},
|
|
275
|
+
runtime,
|
|
361
276
|
};
|
|
362
277
|
}
|
|
363
278
|
|
|
@@ -385,6 +300,39 @@ function isExtensionFile(name: string): boolean {
|
|
|
385
300
|
return name.endsWith(".ts") || name.endsWith(".js");
|
|
386
301
|
}
|
|
387
302
|
|
|
303
|
+
/**
|
|
304
|
+
* Resolve extension entry points from a directory.
|
|
305
|
+
*/
|
|
306
|
+
function resolveExtensionEntries(dir: string): string[] | null {
|
|
307
|
+
const packageJsonPath = path.join(dir, "package.json");
|
|
308
|
+
if (existsSync(packageJsonPath)) {
|
|
309
|
+
const manifest = readExtensionManifest(packageJsonPath);
|
|
310
|
+
if (manifest?.extensions?.length) {
|
|
311
|
+
const entries: string[] = [];
|
|
312
|
+
for (const extPath of manifest.extensions) {
|
|
313
|
+
const resolvedExtPath = path.resolve(dir, extPath);
|
|
314
|
+
if (existsSync(resolvedExtPath)) {
|
|
315
|
+
entries.push(resolvedExtPath);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
if (entries.length > 0) {
|
|
319
|
+
return entries;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const indexTs = path.join(dir, "index.ts");
|
|
325
|
+
const indexJs = path.join(dir, "index.js");
|
|
326
|
+
if (existsSync(indexTs)) {
|
|
327
|
+
return [indexTs];
|
|
328
|
+
}
|
|
329
|
+
if (existsSync(indexJs)) {
|
|
330
|
+
return [indexJs];
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return null;
|
|
334
|
+
}
|
|
335
|
+
|
|
388
336
|
/**
|
|
389
337
|
* Discover extensions in a directory.
|
|
390
338
|
*
|
|
@@ -416,29 +364,9 @@ function discoverExtensionsInDir(dir: string): string[] {
|
|
|
416
364
|
|
|
417
365
|
// 2 & 3. Subdirectories
|
|
418
366
|
if (entry.isDirectory() || entry.isSymbolicLink()) {
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
const manifest = readExtensionManifest(packageJsonPath);
|
|
423
|
-
if (manifest?.extensions) {
|
|
424
|
-
// Load paths declared in manifest (relative to package.json dir)
|
|
425
|
-
for (const extPath of manifest.extensions) {
|
|
426
|
-
const resolvedExtPath = path.resolve(entryPath, extPath);
|
|
427
|
-
if (existsSync(resolvedExtPath)) {
|
|
428
|
-
discovered.push(resolvedExtPath);
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
continue;
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
// Check for index.ts or index.js
|
|
436
|
-
const indexTs = path.join(entryPath, "index.ts");
|
|
437
|
-
const indexJs = path.join(entryPath, "index.js");
|
|
438
|
-
if (existsSync(indexTs)) {
|
|
439
|
-
discovered.push(indexTs);
|
|
440
|
-
} else if (existsSync(indexJs)) {
|
|
441
|
-
discovered.push(indexJs);
|
|
367
|
+
const entries = resolveExtensionEntries(entryPath);
|
|
368
|
+
if (entries) {
|
|
369
|
+
discovered.push(...entries);
|
|
442
370
|
}
|
|
443
371
|
}
|
|
444
372
|
}
|
|
@@ -491,10 +419,20 @@ export async function discoverAndLoadExtensions(
|
|
|
491
419
|
for (const configuredPath of configuredPaths) {
|
|
492
420
|
const resolved = resolvePath(configuredPath, cwd);
|
|
493
421
|
if (existsSync(resolved) && statSync(resolved).isDirectory()) {
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
422
|
+
const entries = resolveExtensionEntries(resolved);
|
|
423
|
+
if (entries) {
|
|
424
|
+
addPaths(entries);
|
|
425
|
+
continue;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
const discovered = discoverExtensionsInDir(resolved);
|
|
429
|
+
if (discovered.length > 0) {
|
|
430
|
+
addPaths(discovered);
|
|
431
|
+
}
|
|
432
|
+
continue;
|
|
497
433
|
}
|
|
434
|
+
|
|
435
|
+
addPath(resolved);
|
|
498
436
|
}
|
|
499
437
|
|
|
500
438
|
return loadExtensions(allPaths, cwd, eventBus);
|