@aexol/spectral 0.7.1 → 0.7.5
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 +5 -0
- package/dist/agent/agents.js +1 -1
- package/dist/agent/index.js +199 -184
- package/dist/commands/serve.js +0 -3
- package/dist/designer/data/systems/renault/DESIGN.md +1 -1
- package/dist/designer/philosophies.js +668 -0
- package/dist/mcp/sampling-handler.js +1 -1
- package/dist/memory/commands/status.js +1 -1
- package/dist/memory/compaction.js +2 -2
- package/dist/memory/config.js +1 -1
- package/dist/memory/debug-log.js +1 -1
- package/dist/memory/hooks/compaction-hook.js +29 -0
- package/dist/memory/index.js +2 -0
- package/dist/memory/observer.js +2 -2
- package/dist/memory/project-observations-store.js +14 -0
- package/dist/memory/tokens.js +1 -1
- package/dist/memory/tools/read-project-observations.js +82 -0
- package/dist/memory/tools/recall-observation.js +2 -2
- package/dist/pi/agent-core/agent-loop.js +501 -0
- package/dist/pi/agent-core/agent.js +401 -0
- package/dist/pi/agent-core/harness/agent-harness.js +899 -0
- package/dist/pi/agent-core/harness/compaction/branch-summarization.js +173 -0
- package/dist/pi/agent-core/harness/compaction/compaction.js +532 -0
- package/dist/pi/agent-core/harness/compaction/utils.js +130 -0
- package/dist/pi/agent-core/harness/env/nodejs.js +485 -0
- package/dist/pi/agent-core/harness/messages.js +101 -0
- package/dist/pi/agent-core/harness/prompt-templates.js +229 -0
- package/dist/pi/agent-core/harness/session/jsonl-repo.js +100 -0
- package/dist/pi/agent-core/harness/session/jsonl-storage.js +230 -0
- package/dist/pi/agent-core/harness/session/memory-repo.js +41 -0
- package/dist/pi/agent-core/harness/session/memory-storage.js +113 -0
- package/dist/pi/agent-core/harness/session/repo-utils.js +38 -0
- package/dist/pi/agent-core/harness/session/session.js +196 -0
- package/dist/pi/agent-core/harness/session/uuid.js +49 -0
- package/dist/pi/agent-core/harness/skills.js +310 -0
- package/dist/pi/agent-core/harness/system-prompt.js +29 -0
- package/dist/pi/agent-core/harness/types.js +93 -0
- package/dist/pi/agent-core/harness/utils/shell-output.js +125 -0
- package/dist/pi/agent-core/harness/utils/truncate.js +289 -0
- package/dist/pi/agent-core/index.js +24 -0
- package/dist/pi/agent-core/node.js +2 -0
- package/dist/pi/agent-core/proxy.js +277 -0
- package/dist/pi/agent-core/types.js +1 -0
- package/dist/pi/ai/api-registry.js +43 -0
- package/dist/pi/ai/cli.js +120 -0
- package/dist/pi/ai/env-api-keys.js +169 -0
- package/dist/pi/ai/image-models.generated.js +441 -0
- package/dist/pi/ai/image-models.js +22 -0
- package/dist/pi/ai/images-api-registry.js +21 -0
- package/dist/pi/ai/images.js +13 -0
- package/dist/pi/ai/index.js +18 -0
- package/dist/pi/ai/models.generated.js +16220 -0
- package/dist/pi/ai/models.js +70 -0
- package/dist/pi/ai/oauth.js +1 -0
- package/dist/pi/ai/providers/anthropic.js +945 -0
- package/dist/pi/ai/providers/faux.js +367 -0
- package/dist/pi/ai/providers/github-copilot-headers.js +28 -0
- package/dist/pi/ai/providers/openai-completions.js +945 -0
- package/dist/pi/ai/providers/openai-prompt-cache.js +9 -0
- package/dist/pi/ai/providers/register-builtins.js +97 -0
- package/dist/pi/ai/providers/simple-options.js +40 -0
- package/dist/pi/ai/providers/transform-messages.js +183 -0
- package/dist/pi/ai/session-resources.js +21 -0
- package/dist/pi/ai/stream.js +26 -0
- package/dist/pi/ai/types.js +1 -0
- package/dist/pi/ai/utils/diagnostics.js +24 -0
- package/dist/pi/ai/utils/event-stream.js +80 -0
- package/dist/pi/ai/utils/hash.js +13 -0
- package/dist/pi/ai/utils/headers.js +7 -0
- package/dist/pi/ai/utils/json-parse.js +112 -0
- package/dist/pi/ai/utils/node-http-proxy.js +96 -0
- package/dist/pi/ai/utils/oauth/anthropic.js +334 -0
- package/dist/pi/ai/utils/oauth/device-code.js +54 -0
- package/dist/pi/ai/utils/oauth/github-copilot.js +270 -0
- package/dist/pi/ai/utils/oauth/index.js +121 -0
- package/dist/pi/ai/utils/oauth/oauth-page.js +104 -0
- package/dist/pi/ai/utils/oauth/openai-codex.js +384 -0
- package/dist/pi/ai/utils/oauth/pkce.js +30 -0
- package/dist/pi/ai/utils/oauth/types.js +1 -0
- package/dist/pi/ai/utils/overflow.js +150 -0
- package/dist/pi/ai/utils/sanitize-unicode.js +25 -0
- package/dist/pi/ai/utils/typebox-helpers.js +20 -0
- package/dist/pi/ai/utils/validation.js +280 -0
- package/dist/pi/coding-agent/bun/cli.js +7 -0
- package/dist/pi/coding-agent/bun/restore-sandbox-env.js +31 -0
- package/dist/pi/coding-agent/cli/args.js +340 -0
- package/dist/pi/coding-agent/cli/file-processor.js +82 -0
- package/dist/pi/coding-agent/cli/initial-message.js +21 -0
- package/dist/pi/coding-agent/cli.js +17 -0
- package/dist/pi/coding-agent/config.js +414 -0
- package/dist/pi/coding-agent/core/agent-session-runtime.js +299 -0
- package/dist/pi/coding-agent/core/agent-session-services.js +117 -0
- package/dist/pi/coding-agent/core/agent-session.js +2498 -0
- package/dist/pi/coding-agent/core/auth-guidance.js +20 -0
- package/dist/pi/coding-agent/core/auth-storage.js +441 -0
- package/dist/pi/coding-agent/core/bash-executor.js +110 -0
- package/dist/pi/coding-agent/core/compaction/branch-summarization.js +242 -0
- package/dist/pi/coding-agent/core/compaction/compaction.js +624 -0
- package/dist/pi/coding-agent/core/compaction/index.js +6 -0
- package/dist/pi/coding-agent/core/compaction/utils.js +152 -0
- package/dist/pi/coding-agent/core/defaults.js +1 -0
- package/dist/pi/coding-agent/core/diagnostics.js +1 -0
- package/dist/pi/coding-agent/core/event-bus.js +24 -0
- package/dist/pi/coding-agent/core/exec.js +74 -0
- package/dist/pi/coding-agent/core/export-html/ansi-to-html.js +248 -0
- package/dist/pi/coding-agent/core/export-html/index.js +225 -0
- package/dist/pi/coding-agent/core/export-html/tool-renderer.js +107 -0
- package/dist/pi/coding-agent/core/extensions/index.js +8 -0
- package/dist/pi/coding-agent/core/extensions/loader.js +485 -0
- package/dist/pi/coding-agent/core/extensions/runner.js +824 -0
- package/dist/pi/coding-agent/core/extensions/types.js +44 -0
- package/dist/pi/coding-agent/core/extensions/wrapper.js +21 -0
- package/dist/pi/coding-agent/core/footer-data-provider.js +309 -0
- package/dist/pi/coding-agent/core/http-dispatcher.js +47 -0
- package/dist/pi/coding-agent/core/index.js +11 -0
- package/dist/pi/coding-agent/core/keybindings.js +294 -0
- package/dist/pi/coding-agent/core/messages.js +122 -0
- package/dist/pi/coding-agent/core/model-registry.js +728 -0
- package/dist/pi/coding-agent/core/model-resolver.js +494 -0
- package/dist/pi/coding-agent/core/output-guard.js +58 -0
- package/dist/pi/coding-agent/core/package-manager.js +2020 -0
- package/dist/pi/coding-agent/core/prompt-templates.js +237 -0
- package/dist/pi/coding-agent/core/provider-display-names.js +32 -0
- package/dist/pi/coding-agent/core/resolve-config-value.js +125 -0
- package/dist/pi/coding-agent/core/resource-loader.js +733 -0
- package/dist/pi/coding-agent/core/sdk.js +282 -0
- package/dist/pi/coding-agent/core/session-cwd.js +37 -0
- package/dist/pi/coding-agent/core/session-manager.js +1146 -0
- package/dist/pi/coding-agent/core/settings-manager.js +794 -0
- package/dist/pi/coding-agent/core/skills.js +386 -0
- package/dist/pi/coding-agent/core/slash-commands.js +24 -0
- package/dist/pi/coding-agent/core/source-info.js +18 -0
- package/dist/pi/coding-agent/core/system-prompt.js +122 -0
- package/dist/pi/coding-agent/core/telemetry.js +8 -0
- package/dist/pi/coding-agent/core/timings.js +30 -0
- package/dist/pi/coding-agent/core/tools/bash.js +341 -0
- package/dist/pi/coding-agent/core/tools/edit-diff.js +344 -0
- package/dist/pi/coding-agent/core/tools/edit.js +324 -0
- package/dist/pi/coding-agent/core/tools/file-mutation-queue.js +36 -0
- package/dist/pi/coding-agent/core/tools/find.js +297 -0
- package/dist/pi/coding-agent/core/tools/grep.js +303 -0
- package/dist/pi/coding-agent/core/tools/index.js +111 -0
- package/dist/pi/coding-agent/core/tools/ls.js +168 -0
- package/dist/pi/coding-agent/core/tools/output-accumulator.js +183 -0
- package/dist/pi/coding-agent/core/tools/path-utils.js +61 -0
- package/dist/pi/coding-agent/core/tools/read.js +288 -0
- package/dist/pi/coding-agent/core/tools/render-utils.js +48 -0
- package/dist/pi/coding-agent/core/tools/tool-definition-wrapper.js +33 -0
- package/dist/pi/coding-agent/core/tools/truncate.js +214 -0
- package/dist/pi/coding-agent/core/tools/write.js +212 -0
- package/dist/pi/coding-agent/index.js +41 -0
- package/dist/pi/coding-agent/main.js +5 -0
- package/dist/pi/coding-agent/migrations.js +280 -0
- package/dist/pi/coding-agent/modes/index.js +7 -0
- package/dist/pi/coding-agent/modes/interactive/components/diff.js +132 -0
- package/dist/pi/coding-agent/modes/interactive/components/keybinding-hints.js +35 -0
- package/dist/pi/coding-agent/modes/interactive/components/visual-truncate.js +32 -0
- package/dist/pi/coding-agent/modes/interactive/interactive-mode.js +3 -0
- package/dist/pi/coding-agent/modes/interactive/theme/theme.js +1023 -0
- package/dist/pi/coding-agent/modes/print-mode.js +130 -0
- package/dist/pi/coding-agent/modes/rpc/jsonl.js +48 -0
- package/dist/pi/coding-agent/modes/rpc/rpc-client.js +409 -0
- package/dist/pi/coding-agent/modes/rpc/rpc-mode.js +600 -0
- package/dist/pi/coding-agent/modes/rpc/rpc-types.js +7 -0
- package/dist/pi/coding-agent/utils/ansi.js +51 -0
- package/dist/pi/coding-agent/utils/changelog.js +86 -0
- package/dist/pi/coding-agent/utils/child-process.js +87 -0
- package/dist/pi/coding-agent/utils/clipboard-image.js +244 -0
- package/dist/pi/coding-agent/utils/clipboard-native.js +13 -0
- package/dist/pi/coding-agent/utils/clipboard.js +116 -0
- package/dist/pi/coding-agent/utils/exif-orientation.js +157 -0
- package/dist/pi/coding-agent/utils/frontmatter.js +25 -0
- package/dist/pi/coding-agent/utils/fs-watch.js +24 -0
- package/dist/pi/coding-agent/utils/git.js +162 -0
- package/dist/pi/coding-agent/utils/html.js +39 -0
- package/dist/pi/coding-agent/utils/image-convert.js +38 -0
- package/dist/pi/coding-agent/utils/image-resize.js +136 -0
- package/dist/pi/coding-agent/utils/mime.js +68 -0
- package/dist/pi/coding-agent/utils/paths.js +91 -0
- package/dist/pi/coding-agent/utils/photon.js +120 -0
- package/dist/pi/coding-agent/utils/pi-user-agent.js +4 -0
- package/dist/pi/coding-agent/utils/shell.js +194 -0
- package/dist/pi/coding-agent/utils/sleep.js +16 -0
- package/dist/pi/coding-agent/utils/syntax-highlight.js +117 -0
- package/dist/pi/coding-agent/utils/tools-manager.js +327 -0
- package/dist/pi/coding-agent/utils/version-check.js +81 -0
- package/dist/pi/coding-agent/utils/windows-self-update.js +76 -0
- package/dist/pi/tui/autocomplete.js +631 -0
- package/dist/pi/tui/components/box.js +103 -0
- package/dist/pi/tui/components/cancellable-loader.js +34 -0
- package/dist/pi/tui/components/editor.js +1915 -0
- package/dist/pi/tui/components/image.js +88 -0
- package/dist/pi/tui/components/input.js +425 -0
- package/dist/pi/tui/components/loader.js +68 -0
- package/dist/pi/tui/components/markdown.js +633 -0
- package/dist/pi/tui/components/select-list.js +158 -0
- package/dist/pi/tui/components/settings-list.js +184 -0
- package/dist/pi/tui/components/spacer.js +22 -0
- package/dist/pi/tui/components/text.js +88 -0
- package/dist/pi/tui/components/truncated-text.js +50 -0
- package/dist/pi/tui/editor-component.js +1 -0
- package/dist/pi/tui/fuzzy.js +109 -0
- package/dist/pi/tui/index.js +31 -0
- package/dist/pi/tui/keybindings.js +173 -0
- package/dist/pi/tui/keys.js +1172 -0
- package/dist/pi/tui/kill-ring.js +43 -0
- package/dist/pi/tui/stdin-buffer.js +360 -0
- package/dist/pi/tui/terminal-image.js +335 -0
- package/dist/pi/tui/terminal.js +324 -0
- package/dist/pi/tui/tui.js +1076 -0
- package/dist/pi/tui/undo-stack.js +24 -0
- package/dist/pi/tui/utils.js +1016 -0
- package/dist/relay/dispatcher.js +30 -0
- package/dist/server/handlers/queue.js +52 -0
- package/dist/server/pi-bridge.js +9 -1
- package/dist/server/session-stream.js +76 -111
- package/dist/server/storage.js +154 -2
- package/dist/server/title-generator.js +14 -153
- package/package.json +24 -6
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extension loader - loads TypeScript extension modules using jiti.
|
|
3
|
+
*
|
|
4
|
+
*/
|
|
5
|
+
import * as fs from "node:fs";
|
|
6
|
+
import { createRequire } from "node:module";
|
|
7
|
+
import * as path from "node:path";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
import * as _bundledPiAgentCore from "../../../agent-core/index.js";
|
|
10
|
+
import * as _bundledPiAi from "../../../ai/index.js";
|
|
11
|
+
import * as _bundledPiAiOauth from "../../../ai/oauth.js";
|
|
12
|
+
import * as _bundledPiTui from "../../../tui/index.js";
|
|
13
|
+
import { createJiti } from "@mariozechner/jiti";
|
|
14
|
+
// Static imports of packages that extensions may use.
|
|
15
|
+
// These MUST be static so Bun bundles them into the compiled binary.
|
|
16
|
+
// The virtualModules option then makes them available to extensions.
|
|
17
|
+
import * as _bundledTypebox from "typebox";
|
|
18
|
+
import * as _bundledTypeboxCompile from "typebox/compile";
|
|
19
|
+
import * as _bundledTypeboxValue from "typebox/value";
|
|
20
|
+
import { CONFIG_DIR_NAME, getAgentDir, isBunBinary } from "../../config.js";
|
|
21
|
+
// NOTE: This import works because loader.ts exports are NOT re-exported from index.ts,
|
|
22
|
+
// avoiding a circular dependency. Extensions can import from ../../index.ts.
|
|
23
|
+
import * as _bundledPiCodingAgent from "../../index.js";
|
|
24
|
+
import { resolvePath } from "../../utils/paths.js";
|
|
25
|
+
import { createEventBus } from "../event-bus.js";
|
|
26
|
+
import { execCommand } from "../exec.js";
|
|
27
|
+
import { createSyntheticSourceInfo } from "../source-info.js";
|
|
28
|
+
/** Modules available to extensions via virtualModules (for compiled Bun binary) */
|
|
29
|
+
const VIRTUAL_MODULES = {
|
|
30
|
+
typebox: _bundledTypebox,
|
|
31
|
+
"typebox/compile": _bundledTypeboxCompile,
|
|
32
|
+
"typebox/value": _bundledTypeboxValue,
|
|
33
|
+
"@sinclair/typebox": _bundledTypebox,
|
|
34
|
+
"@sinclair/typebox/compile": _bundledTypeboxCompile,
|
|
35
|
+
"@sinclair/typebox/value": _bundledTypeboxValue,
|
|
36
|
+
"../../../agent-core/index.ts": _bundledPiAgentCore,
|
|
37
|
+
"../../../tui/index.ts": _bundledPiTui,
|
|
38
|
+
"../../../ai/index.ts": _bundledPiAi,
|
|
39
|
+
"../../../ai/oauth.ts": _bundledPiAiOauth,
|
|
40
|
+
"../../index.ts": _bundledPiCodingAgent,
|
|
41
|
+
"@mariozechner/pi-agent": _bundledPiAgentCore,
|
|
42
|
+
"@mariozechner/pi-tui": _bundledPiTui,
|
|
43
|
+
"@mariozechner/pi-ai": _bundledPiAi,
|
|
44
|
+
"@mariozechner/pi-ai/oauth": _bundledPiAiOauth,
|
|
45
|
+
"@mariozechner/pi-coding-agent": _bundledPiCodingAgent,
|
|
46
|
+
};
|
|
47
|
+
const require = createRequire(import.meta.url);
|
|
48
|
+
/**
|
|
49
|
+
* Get aliases for jiti (used in Node.js/development mode).
|
|
50
|
+
* In Bun binary mode, virtualModules is used instead.
|
|
51
|
+
*/
|
|
52
|
+
let _aliases = null;
|
|
53
|
+
function getAliases() {
|
|
54
|
+
if (_aliases)
|
|
55
|
+
return _aliases;
|
|
56
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
57
|
+
const packageIndex = path.resolve(__dirname, "../..", "index.js");
|
|
58
|
+
const typeboxEntry = require.resolve("typebox");
|
|
59
|
+
const typeboxCompileEntry = require.resolve("typebox/compile");
|
|
60
|
+
const typeboxValueEntry = require.resolve("typebox/value");
|
|
61
|
+
const packagesRoot = path.resolve(__dirname, "../../../../");
|
|
62
|
+
const resolveWorkspaceOrImport = (workspaceRelativePath, specifier) => {
|
|
63
|
+
const workspacePath = path.join(packagesRoot, workspaceRelativePath);
|
|
64
|
+
if (fs.existsSync(workspacePath)) {
|
|
65
|
+
return workspacePath;
|
|
66
|
+
}
|
|
67
|
+
return fileURLToPath(import.meta.resolve(specifier));
|
|
68
|
+
};
|
|
69
|
+
const piCodingAgentEntry = packageIndex;
|
|
70
|
+
const piAgentCoreEntry = resolveWorkspaceOrImport("agent/dist/index.js", "../../../agent-core/index.ts");
|
|
71
|
+
const piTuiEntry = resolveWorkspaceOrImport("tui/dist/index.js", "../../../tui/index.ts");
|
|
72
|
+
const piAiEntry = resolveWorkspaceOrImport("ai/dist/index.js", "../../../ai/index.ts");
|
|
73
|
+
const piAiOauthEntry = resolveWorkspaceOrImport("ai/dist/oauth.js", "../../../ai/oauth.ts");
|
|
74
|
+
_aliases = {
|
|
75
|
+
"../../index.ts": piCodingAgentEntry,
|
|
76
|
+
"../../../agent-core/index.ts": piAgentCoreEntry,
|
|
77
|
+
"../../../tui/index.ts": piTuiEntry,
|
|
78
|
+
"../../../ai/index.ts": piAiEntry,
|
|
79
|
+
"../../../ai/oauth.ts": piAiOauthEntry,
|
|
80
|
+
"@mariozechner/pi-coding-agent": piCodingAgentEntry,
|
|
81
|
+
"@mariozechner/pi-agent": piAgentCoreEntry,
|
|
82
|
+
"@mariozechner/pi-tui": piTuiEntry,
|
|
83
|
+
"@mariozechner/pi-ai": piAiEntry,
|
|
84
|
+
"@mariozechner/pi-ai/oauth": piAiOauthEntry,
|
|
85
|
+
typebox: typeboxEntry,
|
|
86
|
+
"typebox/compile": typeboxCompileEntry,
|
|
87
|
+
"typebox/value": typeboxValueEntry,
|
|
88
|
+
"@sinclair/typebox": typeboxEntry,
|
|
89
|
+
"@sinclair/typebox/compile": typeboxCompileEntry,
|
|
90
|
+
"@sinclair/typebox/value": typeboxValueEntry,
|
|
91
|
+
};
|
|
92
|
+
return _aliases;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Create a runtime with throwing stubs for action methods.
|
|
96
|
+
* Runner.bindCore() replaces these with real implementations.
|
|
97
|
+
*/
|
|
98
|
+
export function createExtensionRuntime() {
|
|
99
|
+
const notInitialized = () => {
|
|
100
|
+
throw new Error("Extension runtime not initialized. Action methods cannot be called during extension loading.");
|
|
101
|
+
};
|
|
102
|
+
const state = {};
|
|
103
|
+
const assertActive = () => {
|
|
104
|
+
if (state.staleMessage) {
|
|
105
|
+
throw new Error(state.staleMessage);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
const runtime = {
|
|
109
|
+
sendMessage: notInitialized,
|
|
110
|
+
sendUserMessage: notInitialized,
|
|
111
|
+
appendEntry: notInitialized,
|
|
112
|
+
setSessionName: notInitialized,
|
|
113
|
+
getSessionName: notInitialized,
|
|
114
|
+
setLabel: notInitialized,
|
|
115
|
+
getActiveTools: notInitialized,
|
|
116
|
+
getAllTools: notInitialized,
|
|
117
|
+
setActiveTools: notInitialized,
|
|
118
|
+
// registerTool() is valid during extension load; refresh is only needed post-bind.
|
|
119
|
+
refreshTools: () => { },
|
|
120
|
+
getCommands: notInitialized,
|
|
121
|
+
getToolDefinition: notInitialized,
|
|
122
|
+
setModel: () => Promise.reject(new Error("Extension runtime not initialized")),
|
|
123
|
+
getThinkingLevel: notInitialized,
|
|
124
|
+
setThinkingLevel: notInitialized,
|
|
125
|
+
flagValues: new Map(),
|
|
126
|
+
pendingProviderRegistrations: [],
|
|
127
|
+
assertActive,
|
|
128
|
+
invalidate: (message) => {
|
|
129
|
+
state.staleMessage ??=
|
|
130
|
+
message ??
|
|
131
|
+
"This extension ctx is stale after session replacement or reload. Do not use a captured pi or command ctx after ctx.newSession(), ctx.fork(), ctx.switchSession(), or ctx.reload(). For newSession, fork, and switchSession, move post-replacement work into withSession and use the ctx passed to withSession. For reload, do not use the old ctx after await ctx.reload().";
|
|
132
|
+
},
|
|
133
|
+
// Pre-bind: queue registrations so bindCore() can flush them once the
|
|
134
|
+
// model registry is available. bindCore() replaces both with direct calls.
|
|
135
|
+
registerProvider: (name, config, extensionPath = "<unknown>") => {
|
|
136
|
+
runtime.pendingProviderRegistrations.push({ name, config, extensionPath });
|
|
137
|
+
},
|
|
138
|
+
unregisterProvider: (name) => {
|
|
139
|
+
runtime.pendingProviderRegistrations = runtime.pendingProviderRegistrations.filter((r) => r.name !== name);
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
return runtime;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Create the ExtensionAPI for an extension.
|
|
146
|
+
* Registration methods write to the extension object.
|
|
147
|
+
* Action methods delegate to the shared runtime.
|
|
148
|
+
*/
|
|
149
|
+
function createExtensionAPI(extension, runtime, cwd, eventBus) {
|
|
150
|
+
const api = {
|
|
151
|
+
// Registration methods - write to extension
|
|
152
|
+
on(event, handler) {
|
|
153
|
+
runtime.assertActive();
|
|
154
|
+
const list = extension.handlers.get(event) ?? [];
|
|
155
|
+
list.push(handler);
|
|
156
|
+
extension.handlers.set(event, list);
|
|
157
|
+
},
|
|
158
|
+
registerTool(tool) {
|
|
159
|
+
runtime.assertActive();
|
|
160
|
+
extension.tools.set(tool.name, {
|
|
161
|
+
definition: tool,
|
|
162
|
+
sourceInfo: extension.sourceInfo,
|
|
163
|
+
});
|
|
164
|
+
runtime.refreshTools();
|
|
165
|
+
},
|
|
166
|
+
registerCommand(name, options) {
|
|
167
|
+
runtime.assertActive();
|
|
168
|
+
extension.commands.set(name, {
|
|
169
|
+
name,
|
|
170
|
+
sourceInfo: extension.sourceInfo,
|
|
171
|
+
...options,
|
|
172
|
+
});
|
|
173
|
+
},
|
|
174
|
+
registerShortcut(shortcut, options) {
|
|
175
|
+
runtime.assertActive();
|
|
176
|
+
extension.shortcuts.set(shortcut, { shortcut, extensionPath: extension.path, ...options });
|
|
177
|
+
},
|
|
178
|
+
registerFlag(name, options) {
|
|
179
|
+
runtime.assertActive();
|
|
180
|
+
extension.flags.set(name, { name, extensionPath: extension.path, ...options });
|
|
181
|
+
if (options.default !== undefined && !runtime.flagValues.has(name)) {
|
|
182
|
+
runtime.flagValues.set(name, options.default);
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
registerMessageRenderer(customType, renderer) {
|
|
186
|
+
runtime.assertActive();
|
|
187
|
+
extension.messageRenderers.set(customType, renderer);
|
|
188
|
+
},
|
|
189
|
+
// Flag access - checks extension registered it, reads from runtime
|
|
190
|
+
getFlag(name) {
|
|
191
|
+
runtime.assertActive();
|
|
192
|
+
if (!extension.flags.has(name))
|
|
193
|
+
return undefined;
|
|
194
|
+
return runtime.flagValues.get(name);
|
|
195
|
+
},
|
|
196
|
+
// Action methods - delegate to shared runtime
|
|
197
|
+
sendMessage(message, options) {
|
|
198
|
+
runtime.assertActive();
|
|
199
|
+
runtime.sendMessage(message, options);
|
|
200
|
+
},
|
|
201
|
+
sendUserMessage(content, options) {
|
|
202
|
+
runtime.assertActive();
|
|
203
|
+
runtime.sendUserMessage(content, options);
|
|
204
|
+
},
|
|
205
|
+
appendEntry(customType, data) {
|
|
206
|
+
runtime.assertActive();
|
|
207
|
+
runtime.appendEntry(customType, data);
|
|
208
|
+
},
|
|
209
|
+
setSessionName(name) {
|
|
210
|
+
runtime.assertActive();
|
|
211
|
+
runtime.setSessionName(name);
|
|
212
|
+
},
|
|
213
|
+
getSessionName() {
|
|
214
|
+
runtime.assertActive();
|
|
215
|
+
return runtime.getSessionName();
|
|
216
|
+
},
|
|
217
|
+
setLabel(entryId, label) {
|
|
218
|
+
runtime.assertActive();
|
|
219
|
+
runtime.setLabel(entryId, label);
|
|
220
|
+
},
|
|
221
|
+
exec(command, args, options) {
|
|
222
|
+
runtime.assertActive();
|
|
223
|
+
return execCommand(command, args, options?.cwd ?? cwd, options);
|
|
224
|
+
},
|
|
225
|
+
getActiveTools() {
|
|
226
|
+
runtime.assertActive();
|
|
227
|
+
return runtime.getActiveTools();
|
|
228
|
+
},
|
|
229
|
+
getAllTools() {
|
|
230
|
+
runtime.assertActive();
|
|
231
|
+
return runtime.getAllTools();
|
|
232
|
+
},
|
|
233
|
+
setActiveTools(toolNames) {
|
|
234
|
+
runtime.assertActive();
|
|
235
|
+
runtime.setActiveTools(toolNames);
|
|
236
|
+
},
|
|
237
|
+
getCommands() {
|
|
238
|
+
runtime.assertActive();
|
|
239
|
+
return runtime.getCommands();
|
|
240
|
+
},
|
|
241
|
+
getToolDefinition(name) {
|
|
242
|
+
runtime.assertActive();
|
|
243
|
+
return runtime.getToolDefinition(name);
|
|
244
|
+
},
|
|
245
|
+
setModel(model) {
|
|
246
|
+
runtime.assertActive();
|
|
247
|
+
return runtime.setModel(model);
|
|
248
|
+
},
|
|
249
|
+
getThinkingLevel() {
|
|
250
|
+
runtime.assertActive();
|
|
251
|
+
return runtime.getThinkingLevel();
|
|
252
|
+
},
|
|
253
|
+
setThinkingLevel(level) {
|
|
254
|
+
runtime.assertActive();
|
|
255
|
+
runtime.setThinkingLevel(level);
|
|
256
|
+
},
|
|
257
|
+
registerProvider(name, config) {
|
|
258
|
+
runtime.assertActive();
|
|
259
|
+
runtime.registerProvider(name, config, extension.path);
|
|
260
|
+
},
|
|
261
|
+
unregisterProvider(name) {
|
|
262
|
+
runtime.assertActive();
|
|
263
|
+
runtime.unregisterProvider(name, extension.path);
|
|
264
|
+
},
|
|
265
|
+
events: eventBus,
|
|
266
|
+
};
|
|
267
|
+
return api;
|
|
268
|
+
}
|
|
269
|
+
async function loadExtensionModule(extensionPath) {
|
|
270
|
+
const jiti = createJiti(import.meta.url, {
|
|
271
|
+
moduleCache: false,
|
|
272
|
+
// In Bun binary: use virtualModules for bundled packages (no filesystem resolution)
|
|
273
|
+
// Also disable tryNative so jiti handles ALL imports (not just the entry point)
|
|
274
|
+
// In Node.js/dev: use aliases to resolve to node_modules paths
|
|
275
|
+
...(isBunBinary ? { virtualModules: VIRTUAL_MODULES, tryNative: false } : { alias: getAliases() }),
|
|
276
|
+
});
|
|
277
|
+
const module = await jiti.import(extensionPath, { default: true });
|
|
278
|
+
const factory = module;
|
|
279
|
+
return typeof factory !== "function" ? undefined : factory;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Create an Extension object with empty collections.
|
|
283
|
+
*/
|
|
284
|
+
function createExtension(extensionPath, resolvedPath) {
|
|
285
|
+
const source = extensionPath.startsWith("<") && extensionPath.endsWith(">")
|
|
286
|
+
? extensionPath.slice(1, -1).split(":")[0] || "temporary"
|
|
287
|
+
: "local";
|
|
288
|
+
const baseDir = extensionPath.startsWith("<") ? undefined : path.dirname(resolvedPath);
|
|
289
|
+
return {
|
|
290
|
+
path: extensionPath,
|
|
291
|
+
resolvedPath,
|
|
292
|
+
sourceInfo: createSyntheticSourceInfo(extensionPath, { source, baseDir }),
|
|
293
|
+
handlers: new Map(),
|
|
294
|
+
tools: new Map(),
|
|
295
|
+
messageRenderers: new Map(),
|
|
296
|
+
commands: new Map(),
|
|
297
|
+
flags: new Map(),
|
|
298
|
+
shortcuts: new Map(),
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
async function loadExtension(extensionPath, cwd, eventBus, runtime) {
|
|
302
|
+
const resolvedPath = resolvePath(extensionPath, cwd, { normalizeUnicodeSpaces: true });
|
|
303
|
+
try {
|
|
304
|
+
const factory = await loadExtensionModule(resolvedPath);
|
|
305
|
+
if (!factory) {
|
|
306
|
+
return { extension: null, error: `Extension does not export a valid factory function: ${extensionPath}` };
|
|
307
|
+
}
|
|
308
|
+
const extension = createExtension(extensionPath, resolvedPath);
|
|
309
|
+
const api = createExtensionAPI(extension, runtime, cwd, eventBus);
|
|
310
|
+
await factory(api);
|
|
311
|
+
return { extension, error: null };
|
|
312
|
+
}
|
|
313
|
+
catch (err) {
|
|
314
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
315
|
+
return { extension: null, error: `Failed to load extension: ${message}` };
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Create an Extension from an inline factory function.
|
|
320
|
+
*/
|
|
321
|
+
export async function loadExtensionFromFactory(factory, cwd, eventBus, runtime, extensionPath = "<inline>") {
|
|
322
|
+
const extension = createExtension(extensionPath, extensionPath);
|
|
323
|
+
const resolvedCwd = resolvePath(cwd);
|
|
324
|
+
const api = createExtensionAPI(extension, runtime, resolvedCwd, eventBus);
|
|
325
|
+
await factory(api);
|
|
326
|
+
return extension;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Load extensions from paths.
|
|
330
|
+
*/
|
|
331
|
+
export async function loadExtensions(paths, cwd, eventBus) {
|
|
332
|
+
const extensions = [];
|
|
333
|
+
const errors = [];
|
|
334
|
+
const resolvedCwd = resolvePath(cwd);
|
|
335
|
+
const resolvedEventBus = eventBus ?? createEventBus();
|
|
336
|
+
const runtime = createExtensionRuntime();
|
|
337
|
+
for (const extPath of paths) {
|
|
338
|
+
const { extension, error } = await loadExtension(extPath, resolvedCwd, resolvedEventBus, runtime);
|
|
339
|
+
if (error) {
|
|
340
|
+
errors.push({ path: extPath, error });
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
if (extension) {
|
|
344
|
+
extensions.push(extension);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return {
|
|
348
|
+
extensions,
|
|
349
|
+
errors,
|
|
350
|
+
runtime,
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
function readPiManifest(packageJsonPath) {
|
|
354
|
+
try {
|
|
355
|
+
const content = fs.readFileSync(packageJsonPath, "utf-8");
|
|
356
|
+
const pkg = JSON.parse(content);
|
|
357
|
+
if (pkg.pi && typeof pkg.pi === "object") {
|
|
358
|
+
return pkg.pi;
|
|
359
|
+
}
|
|
360
|
+
return null;
|
|
361
|
+
}
|
|
362
|
+
catch {
|
|
363
|
+
return null;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
function isExtensionFile(name) {
|
|
367
|
+
return name.endsWith(".ts") || name.endsWith(".js");
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Resolve extension entry points from a directory.
|
|
371
|
+
*
|
|
372
|
+
* Checks for:
|
|
373
|
+
* 1. package.json with "pi.extensions" field -> returns declared paths
|
|
374
|
+
* 2. index.ts or index.js -> returns the index file
|
|
375
|
+
*
|
|
376
|
+
* Returns resolved paths or null if no entry points found.
|
|
377
|
+
*/
|
|
378
|
+
function resolveExtensionEntries(dir) {
|
|
379
|
+
// Check for package.json with "pi" field first
|
|
380
|
+
const packageJsonPath = path.join(dir, "package.json");
|
|
381
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
382
|
+
const manifest = readPiManifest(packageJsonPath);
|
|
383
|
+
if (manifest?.extensions?.length) {
|
|
384
|
+
const entries = [];
|
|
385
|
+
for (const extPath of manifest.extensions) {
|
|
386
|
+
const resolvedExtPath = path.resolve(dir, extPath);
|
|
387
|
+
if (fs.existsSync(resolvedExtPath)) {
|
|
388
|
+
entries.push(resolvedExtPath);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
if (entries.length > 0) {
|
|
392
|
+
return entries;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
// Check for index.ts or index.js
|
|
397
|
+
const indexTs = path.join(dir, "index.ts");
|
|
398
|
+
const indexJs = path.join(dir, "index.js");
|
|
399
|
+
if (fs.existsSync(indexTs)) {
|
|
400
|
+
return [indexTs];
|
|
401
|
+
}
|
|
402
|
+
if (fs.existsSync(indexJs)) {
|
|
403
|
+
return [indexJs];
|
|
404
|
+
}
|
|
405
|
+
return null;
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Discover extensions in a directory.
|
|
409
|
+
*
|
|
410
|
+
* Discovery rules:
|
|
411
|
+
* 1. Direct files: `extensions/*.ts` or `*.js` → load
|
|
412
|
+
* 2. Subdirectory with index: `extensions/* /index.ts` or `index.js` → load
|
|
413
|
+
* 3. Subdirectory with package.json: `extensions/* /package.json` with "pi" field → load what it declares
|
|
414
|
+
*
|
|
415
|
+
* No recursion beyond one level. Complex packages must use package.json manifest.
|
|
416
|
+
*/
|
|
417
|
+
function discoverExtensionsInDir(dir) {
|
|
418
|
+
if (!fs.existsSync(dir)) {
|
|
419
|
+
return [];
|
|
420
|
+
}
|
|
421
|
+
const discovered = [];
|
|
422
|
+
try {
|
|
423
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
424
|
+
for (const entry of entries) {
|
|
425
|
+
const entryPath = path.join(dir, entry.name);
|
|
426
|
+
// 1. Direct files: *.ts or *.js
|
|
427
|
+
if ((entry.isFile() || entry.isSymbolicLink()) && isExtensionFile(entry.name)) {
|
|
428
|
+
discovered.push(entryPath);
|
|
429
|
+
continue;
|
|
430
|
+
}
|
|
431
|
+
// 2 & 3. Subdirectories
|
|
432
|
+
if (entry.isDirectory() || entry.isSymbolicLink()) {
|
|
433
|
+
const entries = resolveExtensionEntries(entryPath);
|
|
434
|
+
if (entries) {
|
|
435
|
+
discovered.push(...entries);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
catch {
|
|
441
|
+
return [];
|
|
442
|
+
}
|
|
443
|
+
return discovered;
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Discover and load extensions from standard locations.
|
|
447
|
+
*/
|
|
448
|
+
export async function discoverAndLoadExtensions(configuredPaths, cwd, agentDir = getAgentDir(), eventBus) {
|
|
449
|
+
const resolvedCwd = resolvePath(cwd);
|
|
450
|
+
const resolvedAgentDir = resolvePath(agentDir);
|
|
451
|
+
const allPaths = [];
|
|
452
|
+
const seen = new Set();
|
|
453
|
+
const addPaths = (paths) => {
|
|
454
|
+
for (const p of paths) {
|
|
455
|
+
const resolved = path.resolve(p);
|
|
456
|
+
if (!seen.has(resolved)) {
|
|
457
|
+
seen.add(resolved);
|
|
458
|
+
allPaths.push(p);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
};
|
|
462
|
+
// 1. Project-local extensions: cwd/${CONFIG_DIR_NAME}/extensions/
|
|
463
|
+
const localExtDir = path.join(resolvedCwd, CONFIG_DIR_NAME, "extensions");
|
|
464
|
+
addPaths(discoverExtensionsInDir(localExtDir));
|
|
465
|
+
// 2. Global extensions: agentDir/extensions/
|
|
466
|
+
const globalExtDir = path.join(resolvedAgentDir, "extensions");
|
|
467
|
+
addPaths(discoverExtensionsInDir(globalExtDir));
|
|
468
|
+
// 3. Explicitly configured paths
|
|
469
|
+
for (const p of configuredPaths) {
|
|
470
|
+
const resolved = resolvePath(p, resolvedCwd, { normalizeUnicodeSpaces: true });
|
|
471
|
+
if (fs.existsSync(resolved) && fs.statSync(resolved).isDirectory()) {
|
|
472
|
+
// Check for package.json with pi manifest or index.ts
|
|
473
|
+
const entries = resolveExtensionEntries(resolved);
|
|
474
|
+
if (entries) {
|
|
475
|
+
addPaths(entries);
|
|
476
|
+
continue;
|
|
477
|
+
}
|
|
478
|
+
// No explicit entries - discover individual files in directory
|
|
479
|
+
addPaths(discoverExtensionsInDir(resolved));
|
|
480
|
+
continue;
|
|
481
|
+
}
|
|
482
|
+
addPaths([resolved]);
|
|
483
|
+
}
|
|
484
|
+
return loadExtensions(allPaths, resolvedCwd, eventBus);
|
|
485
|
+
}
|