@aexol/spectral 0.0.8 → 0.1.2
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/dist/server/pi-bridge.js +64 -4
- package/package.json +2 -1
package/dist/server/pi-bridge.js
CHANGED
|
@@ -48,7 +48,11 @@
|
|
|
48
48
|
* conversations within a single WS connection work normally.
|
|
49
49
|
*/
|
|
50
50
|
import { AuthStorage, createAgentSession, DefaultResourceLoader, ModelRegistry, SessionManager, } from "@mariozechner/pi-coding-agent";
|
|
51
|
+
import { createJiti } from "@mariozechner/jiti";
|
|
51
52
|
import { randomUUID } from "node:crypto";
|
|
53
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
54
|
+
import { dirname, resolve } from "node:path";
|
|
55
|
+
import { fileURLToPath } from "node:url";
|
|
52
56
|
import aexolMcpExtension from "../extensions/aexol-mcp.js";
|
|
53
57
|
import { fetchAllowedModels as defaultFetchAllowedModels, } from "../relay/models-fetch.js";
|
|
54
58
|
/**
|
|
@@ -86,6 +90,41 @@ function extractTextFromContent(content) {
|
|
|
86
90
|
}
|
|
87
91
|
return out;
|
|
88
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* Resolve the entry point of the pi-mcp-adapter extension (index.ts)
|
|
95
|
+
* by walking up from this file's location through node_modules.
|
|
96
|
+
* Returns the absolute path, or null if the package is not installed.
|
|
97
|
+
*/
|
|
98
|
+
function resolveMcpAdapterEntry() {
|
|
99
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
100
|
+
const rel = "node_modules/pi-mcp-adapter/package.json";
|
|
101
|
+
const root = "/";
|
|
102
|
+
let dir = __dirname;
|
|
103
|
+
for (let i = 0; i < 20; i++) {
|
|
104
|
+
const pkgPath = resolve(dir, rel);
|
|
105
|
+
try {
|
|
106
|
+
const raw = readFileSync(pkgPath, "utf8");
|
|
107
|
+
const pkg = JSON.parse(raw);
|
|
108
|
+
const extRel = pkg.pi?.extensions?.[0];
|
|
109
|
+
if (extRel) {
|
|
110
|
+
return resolve(dirname(pkgPath), extRel);
|
|
111
|
+
}
|
|
112
|
+
// Package found but no pi.extensions — try index.ts as fallback
|
|
113
|
+
const indexTs = resolve(dirname(pkgPath), "index.ts");
|
|
114
|
+
if (existsSync(indexTs))
|
|
115
|
+
return indexTs;
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
// package.json not readable at this level, keep walking up
|
|
120
|
+
}
|
|
121
|
+
const parent = dirname(dir);
|
|
122
|
+
if (parent === dir || parent === root)
|
|
123
|
+
break;
|
|
124
|
+
dir = parent;
|
|
125
|
+
}
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
89
128
|
export class PiBridge {
|
|
90
129
|
session;
|
|
91
130
|
unsubscribe;
|
|
@@ -116,13 +155,34 @@ export class PiBridge {
|
|
|
116
155
|
async start() {
|
|
117
156
|
if (this.disposed)
|
|
118
157
|
throw new Error("PiBridge already disposed");
|
|
119
|
-
|
|
120
|
-
//
|
|
121
|
-
//
|
|
158
|
+
const extensionFactories = [aexolMcpExtension];
|
|
159
|
+
// Load pi-mcp-adapter via jiti so tsc never crawls its .ts files in
|
|
160
|
+
// node_modules. The static `import` was causing tsc to type-check
|
|
161
|
+
// pi-mcp-adapter's source and fail the build on its type errors.
|
|
162
|
+
// jiti is the same loader pi uses internally for all extensions.
|
|
163
|
+
const mcpAdapterPath = resolveMcpAdapterEntry();
|
|
164
|
+
if (mcpAdapterPath) {
|
|
165
|
+
try {
|
|
166
|
+
const jiti = createJiti(import.meta.url, { moduleCache: false });
|
|
167
|
+
const mcpAdapterFactory = await jiti.import(mcpAdapterPath, { default: true });
|
|
168
|
+
if (typeof mcpAdapterFactory === "function") {
|
|
169
|
+
extensionFactories.push(async (pi) => mcpAdapterFactory(pi));
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
console.info("[PiBridge] pi-mcp-adapter not found; standard MCP servers disabled.");
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
console.info("[PiBridge] pi-mcp-adapter not found; standard MCP servers disabled.");
|
|
178
|
+
}
|
|
179
|
+
// ResourceLoader with extensions wired in via factories.
|
|
180
|
+
// Each factory's signature `(pi: ExtensionAPI) => Promise<void>` matches
|
|
181
|
+
// the ExtensionFactory type exactly, so we can pass them directly.
|
|
122
182
|
const resourceLoader = new DefaultResourceLoader({
|
|
123
183
|
cwd: this.opts.cwd,
|
|
124
184
|
agentDir: this.opts.agentDir ?? `${process.env.HOME ?? ""}/.pi/agent`,
|
|
125
|
-
extensionFactories
|
|
185
|
+
extensionFactories,
|
|
126
186
|
// Skip on-disk extension/skill discovery so the server is self-contained.
|
|
127
187
|
noExtensions: false,
|
|
128
188
|
noSkills: true,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aexol/spectral",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Always-on coding agent for Aexol — branded pi wrapper with relay-based browser access.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"@inquirer/prompts": "^7.2.0",
|
|
54
|
+
"@mariozechner/jiti": "^2.6.5",
|
|
54
55
|
"@mariozechner/pi-coding-agent": "^0.70.2",
|
|
55
56
|
"better-sqlite3": "^12.9.0",
|
|
56
57
|
"pi-mcp-adapter": "^2.5.4",
|