openlore 2.0.9 → 2.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -0
- package/dist/cli/commands/mcp.d.ts.map +1 -1
- package/dist/cli/commands/mcp.js +77 -233
- package/dist/cli/commands/mcp.js.map +1 -1
- package/dist/cli/commands/orient.d.ts.map +1 -1
- package/dist/cli/commands/orient.js +17 -0
- package/dist/cli/commands/orient.js.map +1 -1
- package/dist/cli/commands/serve.d.ts +49 -0
- package/dist/cli/commands/serve.d.ts.map +1 -0
- package/dist/cli/commands/serve.js +362 -0
- package/dist/cli/commands/serve.js.map +1 -0
- package/dist/cli/commands/setup.d.ts.map +1 -1
- package/dist/cli/commands/setup.js +23 -7
- package/dist/cli/commands/setup.js.map +1 -1
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/install/templates/agent-instructions.md +1 -3
- package/dist/core/services/mcp-watcher.d.ts +9 -0
- package/dist/core/services/mcp-watcher.d.ts.map +1 -1
- package/dist/core/services/mcp-watcher.js +10 -0
- package/dist/core/services/mcp-watcher.js.map +1 -1
- package/dist/core/services/serve-client.d.ts +40 -0
- package/dist/core/services/serve-client.d.ts.map +1 -0
- package/dist/core/services/serve-client.js +115 -0
- package/dist/core/services/serve-client.js.map +1 -0
- package/dist/core/services/tool-dispatch.d.ts +28 -0
- package/dist/core/services/tool-dispatch.d.ts.map +1 -0
- package/dist/core/services/tool-dispatch.js +246 -0
- package/dist/core/services/tool-dispatch.js.map +1 -0
- package/examples/pi/README.md +70 -0
- package/examples/pi/openlore.ts +358 -0
- package/package.json +1 -1
|
@@ -15,7 +15,19 @@ import { existsSync } from 'node:fs';
|
|
|
15
15
|
import { join } from 'node:path';
|
|
16
16
|
import { logger } from '../../utils/logger.js';
|
|
17
17
|
import { handleOrient } from '../../core/services/mcp-handlers/orient.js';
|
|
18
|
+
import { estimateTokens } from '../../core/services/llm-service.js';
|
|
18
19
|
import { OPENLORE_ANALYSIS_REL_PATH } from '../../constants.js';
|
|
20
|
+
/**
|
|
21
|
+
* Opt-in performance readout (Issue #128). Off by default — nothing is measured
|
|
22
|
+
* or printed unless the caller passes --metrics. Reported to stderr so it never
|
|
23
|
+
* corrupts the JSON on stdout that the skill wrappers parse. Local-only: wall
|
|
24
|
+
* time plus an estimate of the result's output size; no network, no LLM.
|
|
25
|
+
*/
|
|
26
|
+
function reportMetrics(startNs, result) {
|
|
27
|
+
const wallMs = Number(process.hrtime.bigint() - startNs) / 1e6;
|
|
28
|
+
const tokens = estimateTokens(JSON.stringify(result));
|
|
29
|
+
process.stderr.write(`[orient:metrics] wall=${wallMs.toFixed(1)}ms output≈${tokens} tokens (local, no network)\n`);
|
|
30
|
+
}
|
|
19
31
|
/** True once `openlore analyze` has produced an llm-context artifact. */
|
|
20
32
|
function hasAnalysis(directory) {
|
|
21
33
|
return existsSync(join(directory, OPENLORE_ANALYSIS_REL_PATH, 'llm-context.json'));
|
|
@@ -105,11 +117,13 @@ export const orientCommand = new Command('orient')
|
|
|
105
117
|
.option('--token-budget <n>', 'Cap relevantFunctions to ~this many tokens (Spec 25 P4); highest-scored kept, exact duplicates collapsed')
|
|
106
118
|
.option('--lean', 'Return only the navigation core — drop provenance/change-coupling/insertion-points/specs/decisions enrichment (Spec 27)', false)
|
|
107
119
|
.option('--json', 'Emit the full result as JSON instead of a human-readable summary', false)
|
|
120
|
+
.option('--metrics', 'Report wall time and output size to stderr (opt-in; off by default)', false)
|
|
108
121
|
.addHelpText('after', `
|
|
109
122
|
Examples:
|
|
110
123
|
$ openlore orient --task "add a new CLI command"
|
|
111
124
|
$ openlore orient --json --task "fix the analyze cache"
|
|
112
125
|
$ openlore orient --json --task "auth flow" --limit 10
|
|
126
|
+
$ openlore orient --metrics --task "auth flow" # opt-in wall-time/output-size readout
|
|
113
127
|
|
|
114
128
|
Requires "openlore analyze" to have been run at least once. With no --task,
|
|
115
129
|
prints a short session-start primer (used by the install SessionStart hook).
|
|
@@ -142,9 +156,12 @@ prints a short session-start primer (used by the install SessionStart hook).
|
|
|
142
156
|
// In --json mode keep stdout clean (validateDirectory logs to stdout);
|
|
143
157
|
// in human mode let diagnostics through normally.
|
|
144
158
|
const lean = opts.lean ?? false;
|
|
159
|
+
const startNs = opts.metrics ? process.hrtime.bigint() : 0n;
|
|
145
160
|
const result = (asJson
|
|
146
161
|
? await withQuietStdout(() => handleOrient(directory, task, limit, tokenBudget, lean))
|
|
147
162
|
: await handleOrient(directory, task, limit, tokenBudget, lean));
|
|
163
|
+
if (opts.metrics)
|
|
164
|
+
reportMetrics(startNs, result);
|
|
148
165
|
// Always emit structured results (including the "no analysis" error object)
|
|
149
166
|
// on stdout so wrapper scripts can parse them — mirroring the MCP tool.
|
|
150
167
|
if (asJson) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orient.js","sourceRoot":"","sources":["../../../src/cli/commands/orient.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,4CAA4C,CAAC;AAC1E,OAAO,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"orient.js","sourceRoot":"","sources":["../../../src/cli/commands/orient.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* openlore serve — local HTTP daemon (warm, loopback-only).
|
|
3
|
+
*
|
|
4
|
+
* A long-lived process that keeps openlore's caches warm across calls and
|
|
5
|
+
* exposes the tool surface over plain HTTP so non-MCP clients (e.g. a Pi
|
|
6
|
+
* extension) can hit it with `fetch` — no JSON-RPC, no subprocess-per-call.
|
|
7
|
+
*
|
|
8
|
+
* It reuses the SAME tool dispatch as the stdio MCP server
|
|
9
|
+
* ({@link dispatchTool}) so the two transports can't drift, and the SAME tool
|
|
10
|
+
* presets ({@link selectActiveTools}) so a small-model client gets a focused
|
|
11
|
+
* surface (default: `navigation`).
|
|
12
|
+
*
|
|
13
|
+
* Endpoints (all loopback):
|
|
14
|
+
* GET /health → { ok, version, root, preset, tools, uptimeMs }
|
|
15
|
+
* POST /tool/:name body { directory?, args } → handler result (JSON)
|
|
16
|
+
*
|
|
17
|
+
* Discovery: writes `.openlore/serve.json` { port, pid, host, token?, startedAt }
|
|
18
|
+
* in the served root so a client can find and reuse a running daemon.
|
|
19
|
+
*
|
|
20
|
+
* Security: binds 127.0.0.1 only. An optional --token must be presented as the
|
|
21
|
+
* `x-openlore-token` header, keeping other local users off the port.
|
|
22
|
+
*
|
|
23
|
+
* Freshness (watcher + continuous re-analyze) is layered on separately; this
|
|
24
|
+
* module is the transport + lifecycle core.
|
|
25
|
+
*/
|
|
26
|
+
import { Command } from 'commander';
|
|
27
|
+
interface ServeCliOptions {
|
|
28
|
+
directory?: string;
|
|
29
|
+
port?: string;
|
|
30
|
+
host?: string;
|
|
31
|
+
preset?: string;
|
|
32
|
+
token?: string;
|
|
33
|
+
stop?: boolean;
|
|
34
|
+
/** false (via --no-watch) disables the freshness watcher + re-analyze lane. */
|
|
35
|
+
watch?: boolean;
|
|
36
|
+
}
|
|
37
|
+
/** Live daemon handle. Returned by {@link startServe} so callers (tests) can
|
|
38
|
+
* address and shut down the running server without signalling the process. */
|
|
39
|
+
export interface ServeHandle {
|
|
40
|
+
port: number;
|
|
41
|
+
host: string;
|
|
42
|
+
token?: string;
|
|
43
|
+
baseUrl: string;
|
|
44
|
+
close(): Promise<void>;
|
|
45
|
+
}
|
|
46
|
+
export declare function startServe(options: ServeCliOptions): Promise<ServeHandle | undefined>;
|
|
47
|
+
export declare const serveCommand: Command;
|
|
48
|
+
export {};
|
|
49
|
+
//# sourceMappingURL=serve.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/serve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAiCpC,UAAU,eAAe;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,+EAA+E;IAC/E,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;8EAC8E;AAC9E,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAkGD,wBAAsB,UAAU,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CA8M3F;AAED,eAAO,MAAM,YAAY,SA4BrB,CAAC"}
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* openlore serve — local HTTP daemon (warm, loopback-only).
|
|
3
|
+
*
|
|
4
|
+
* A long-lived process that keeps openlore's caches warm across calls and
|
|
5
|
+
* exposes the tool surface over plain HTTP so non-MCP clients (e.g. a Pi
|
|
6
|
+
* extension) can hit it with `fetch` — no JSON-RPC, no subprocess-per-call.
|
|
7
|
+
*
|
|
8
|
+
* It reuses the SAME tool dispatch as the stdio MCP server
|
|
9
|
+
* ({@link dispatchTool}) so the two transports can't drift, and the SAME tool
|
|
10
|
+
* presets ({@link selectActiveTools}) so a small-model client gets a focused
|
|
11
|
+
* surface (default: `navigation`).
|
|
12
|
+
*
|
|
13
|
+
* Endpoints (all loopback):
|
|
14
|
+
* GET /health → { ok, version, root, preset, tools, uptimeMs }
|
|
15
|
+
* POST /tool/:name body { directory?, args } → handler result (JSON)
|
|
16
|
+
*
|
|
17
|
+
* Discovery: writes `.openlore/serve.json` { port, pid, host, token?, startedAt }
|
|
18
|
+
* in the served root so a client can find and reuse a running daemon.
|
|
19
|
+
*
|
|
20
|
+
* Security: binds 127.0.0.1 only. An optional --token must be presented as the
|
|
21
|
+
* `x-openlore-token` header, keeping other local users off the port.
|
|
22
|
+
*
|
|
23
|
+
* Freshness (watcher + continuous re-analyze) is layered on separately; this
|
|
24
|
+
* module is the transport + lifecycle core.
|
|
25
|
+
*/
|
|
26
|
+
import { Command } from 'commander';
|
|
27
|
+
import { createServer } from 'node:http';
|
|
28
|
+
import { createRequire } from 'node:module';
|
|
29
|
+
import { writeFile, readFile, unlink, mkdir } from 'node:fs/promises';
|
|
30
|
+
import { join, resolve } from 'node:path';
|
|
31
|
+
import { logger } from '../../utils/logger.js';
|
|
32
|
+
import { OPENLORE_DIR } from '../../constants.js';
|
|
33
|
+
import { dispatchTool, UnknownToolError } from '../../core/services/tool-dispatch.js';
|
|
34
|
+
import { validateDirectory } from '../../core/services/mcp-handlers/utils.js';
|
|
35
|
+
import { McpWatcher } from '../../core/services/mcp-watcher.js';
|
|
36
|
+
import { openloreAnalyze } from '../../api/analyze.js';
|
|
37
|
+
import { TOOL_DEFINITIONS, TOOL_PRESETS, selectActiveTools } from './mcp.js';
|
|
38
|
+
/**
|
|
39
|
+
* Debounce before a full call-graph re-analyze after edits settle. Longer than
|
|
40
|
+
* the watcher's signature debounce (WATCH_DEBOUNCE_MS=400) because re-analysis
|
|
41
|
+
* is heavier; a few seconds of quiet is the signal that an edit burst is done.
|
|
42
|
+
*/
|
|
43
|
+
const REANALYZE_DEBOUNCE_MS = 4000;
|
|
44
|
+
const _require = createRequire(import.meta.url);
|
|
45
|
+
const _pkgVersion = _require('../../../package.json').version;
|
|
46
|
+
const SERVE_FILE = 'serve.json';
|
|
47
|
+
const MAX_BODY_BYTES = 1_000_000; // tool args are small; reject anything larger
|
|
48
|
+
function serveFilePath(root) {
|
|
49
|
+
return join(root, OPENLORE_DIR, SERVE_FILE);
|
|
50
|
+
}
|
|
51
|
+
/** Read a JSON request body with a hard size ceiling. Rejects on overflow/parse error. */
|
|
52
|
+
function readJsonBody(req) {
|
|
53
|
+
return new Promise((resolve_, reject) => {
|
|
54
|
+
const chunks = [];
|
|
55
|
+
let size = 0;
|
|
56
|
+
req.on('data', (c) => {
|
|
57
|
+
size += c.length;
|
|
58
|
+
if (size > MAX_BODY_BYTES) {
|
|
59
|
+
reject(new Error('request body too large'));
|
|
60
|
+
req.destroy();
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
chunks.push(c);
|
|
64
|
+
});
|
|
65
|
+
req.on('end', () => {
|
|
66
|
+
const raw = Buffer.concat(chunks).toString('utf-8').trim();
|
|
67
|
+
if (!raw)
|
|
68
|
+
return resolve_({});
|
|
69
|
+
try {
|
|
70
|
+
resolve_(JSON.parse(raw));
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
reject(new Error('invalid JSON body'));
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
req.on('error', reject);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
function sendJson(res, status, body) {
|
|
80
|
+
const text = JSON.stringify(body);
|
|
81
|
+
res.writeHead(status, {
|
|
82
|
+
'content-type': 'application/json',
|
|
83
|
+
'content-length': Buffer.byteLength(text),
|
|
84
|
+
});
|
|
85
|
+
res.end(text);
|
|
86
|
+
}
|
|
87
|
+
/** Read <root>/.openlore/serve.json if present. */
|
|
88
|
+
async function readDescriptor(root) {
|
|
89
|
+
try {
|
|
90
|
+
return JSON.parse(await readFile(serveFilePath(root), 'utf-8'));
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Confirm a descriptor points at a LIVE openlore daemon — not a stale serve.json
|
|
98
|
+
* left by a SIGKILL'd process, nor a recycled port now owned by something else.
|
|
99
|
+
* Verifies GET /health returns our `ok: true` shape, so we never signal a PID we
|
|
100
|
+
* can't positively identify as our own daemon.
|
|
101
|
+
*/
|
|
102
|
+
async function daemonAlive(desc) {
|
|
103
|
+
try {
|
|
104
|
+
const res = await fetch(`http://${desc.host}:${desc.port}/health`, {
|
|
105
|
+
signal: AbortSignal.timeout(1000),
|
|
106
|
+
});
|
|
107
|
+
if (!res.ok)
|
|
108
|
+
return false;
|
|
109
|
+
const body = (await res.json().catch(() => null));
|
|
110
|
+
return body?.ok === true;
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/** Stop a daemon previously started for `root` by signalling its recorded pid. */
|
|
117
|
+
async function stopDaemon(root) {
|
|
118
|
+
const path = serveFilePath(root);
|
|
119
|
+
const desc = await readDescriptor(root);
|
|
120
|
+
if (!desc) {
|
|
121
|
+
logger.warning(`No running openlore serve daemon found for ${root}.`);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
// Only signal a PID we've confirmed is our live daemon on the recorded port.
|
|
125
|
+
// A stale serve.json could otherwise point at a recycled PID belonging to an
|
|
126
|
+
// unrelated process — SIGTERM to that would be a nasty surprise.
|
|
127
|
+
if (!(await daemonAlive(desc))) {
|
|
128
|
+
await unlink(path).catch(() => { });
|
|
129
|
+
logger.warning(`No live daemon at ${desc.host}:${desc.port}; removed stale ${SERVE_FILE}.`);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
try {
|
|
133
|
+
process.kill(desc.pid, 'SIGTERM');
|
|
134
|
+
logger.success(`Sent SIGTERM to openlore serve (pid ${desc.pid}).`);
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
await unlink(path).catch(() => { });
|
|
138
|
+
logger.warning(`Daemon pid ${desc.pid} not signalable; removed stale ${SERVE_FILE}.`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
export async function startServe(options) {
|
|
142
|
+
const root = resolve(options.directory ?? process.cwd());
|
|
143
|
+
if (options.stop) {
|
|
144
|
+
await stopDaemon(root);
|
|
145
|
+
return undefined;
|
|
146
|
+
}
|
|
147
|
+
const host = options.host ?? '127.0.0.1';
|
|
148
|
+
const presetName = options.preset ?? 'navigation';
|
|
149
|
+
if (presetName !== 'all' && !TOOL_PRESETS[presetName]) {
|
|
150
|
+
logger.error(`Unknown --preset "${presetName}". Known: ${Object.keys(TOOL_PRESETS).join(', ')}, all.`);
|
|
151
|
+
process.exitCode = 1;
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
// Active tool surface: 'all' = every tool, otherwise the named preset.
|
|
155
|
+
const activeNames = new Set((presetName === 'all'
|
|
156
|
+
? TOOL_DEFINITIONS
|
|
157
|
+
: selectActiveTools(TOOL_DEFINITIONS, { preset: presetName })).map((t) => t.name));
|
|
158
|
+
// Don't start a second daemon for a root already served by a healthy one —
|
|
159
|
+
// a concurrent spawn (two MCP clients, or pi + MCP) would otherwise leave two
|
|
160
|
+
// watchers racing on the same .openlore/analysis. Reuse the live one instead.
|
|
161
|
+
const existing = await readDescriptor(root);
|
|
162
|
+
if (existing && (await daemonAlive(existing))) {
|
|
163
|
+
logger.success(`openlore serve already running for ${root} at http://${existing.host}:${existing.port} — reusing.`);
|
|
164
|
+
return {
|
|
165
|
+
port: existing.port,
|
|
166
|
+
host: existing.host,
|
|
167
|
+
token: existing.token,
|
|
168
|
+
baseUrl: `http://${existing.host}:${existing.port}`,
|
|
169
|
+
close: async () => { }, // never tear down a daemon this process didn't start
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
const token = options.token ?? (process.env.OPENLORE_SERVE_TOKEN || undefined);
|
|
173
|
+
const startedAt = new Date().toISOString();
|
|
174
|
+
const startMs = Date.now();
|
|
175
|
+
const server = createServer((req, res) => {
|
|
176
|
+
void handleRequest(req, res).catch((err) => {
|
|
177
|
+
sendJson(res, 500, { error: err instanceof Error ? err.message : String(err) });
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
async function handleRequest(req, res) {
|
|
181
|
+
const url = new URL(req.url ?? '/', `http://${host}`);
|
|
182
|
+
// Token gate (skips /health so liveness checks need no secret).
|
|
183
|
+
if (token && url.pathname !== '/health') {
|
|
184
|
+
if (req.headers['x-openlore-token'] !== token) {
|
|
185
|
+
sendJson(res, 401, { error: 'invalid or missing x-openlore-token' });
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if (req.method === 'GET' && url.pathname === '/health') {
|
|
190
|
+
sendJson(res, 200, {
|
|
191
|
+
ok: true,
|
|
192
|
+
version: _pkgVersion,
|
|
193
|
+
root,
|
|
194
|
+
preset: presetName,
|
|
195
|
+
tools: [...activeNames],
|
|
196
|
+
uptimeMs: Date.now() - startMs,
|
|
197
|
+
});
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
if (req.method === 'POST' && url.pathname.startsWith('/tool/')) {
|
|
201
|
+
const name = decodeURIComponent(url.pathname.slice('/tool/'.length));
|
|
202
|
+
// The preset is ADVISORY (reported by /health for clients that want a
|
|
203
|
+
// curated list, e.g. the Pi extension). The daemon dispatches any known
|
|
204
|
+
// tool so it can back multiple clients with different surfaces — notably
|
|
205
|
+
// the MCP server, which delegates all ~45 tools here. Unknown tools 404
|
|
206
|
+
// via UnknownToolError below.
|
|
207
|
+
let body;
|
|
208
|
+
try {
|
|
209
|
+
body = await readJsonBody(req);
|
|
210
|
+
}
|
|
211
|
+
catch (err) {
|
|
212
|
+
sendJson(res, 400, { error: err instanceof Error ? err.message : 'bad request' });
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
const args = body.args ?? {};
|
|
216
|
+
// Directory precedence: explicit body.directory → args.directory → served root.
|
|
217
|
+
const directory = (typeof body.directory === 'string' && body.directory)
|
|
218
|
+
|| (typeof args.directory === 'string' && args.directory)
|
|
219
|
+
|| root;
|
|
220
|
+
// Ensure handlers receive directory in args (they read it from there).
|
|
221
|
+
if (typeof args.directory !== 'string')
|
|
222
|
+
args.directory = directory;
|
|
223
|
+
try {
|
|
224
|
+
await validateDirectory(directory);
|
|
225
|
+
}
|
|
226
|
+
catch (err) {
|
|
227
|
+
sendJson(res, 400, { error: err instanceof Error ? err.message : 'invalid directory' });
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
try {
|
|
231
|
+
const result = await dispatchTool(name, args, directory);
|
|
232
|
+
sendJson(res, 200, result ?? null);
|
|
233
|
+
}
|
|
234
|
+
catch (err) {
|
|
235
|
+
if (err instanceof UnknownToolError) {
|
|
236
|
+
sendJson(res, 404, { error: err.message });
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
sendJson(res, 500, { error: err instanceof Error ? err.message : String(err) });
|
|
240
|
+
}
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
sendJson(res, 404, { error: `No route for ${req.method} ${url.pathname}` });
|
|
244
|
+
}
|
|
245
|
+
// Bind (port 0 → OS picks a free ephemeral port).
|
|
246
|
+
const port = options.port ? parseInt(options.port, 10) : 0;
|
|
247
|
+
await new Promise((resolve_, reject) => {
|
|
248
|
+
server.once('error', reject);
|
|
249
|
+
server.listen(port, host, resolve_);
|
|
250
|
+
});
|
|
251
|
+
const addr = server.address();
|
|
252
|
+
const boundPort = typeof addr === 'object' && addr ? addr.port : port;
|
|
253
|
+
const descriptor = {
|
|
254
|
+
port: boundPort,
|
|
255
|
+
pid: process.pid,
|
|
256
|
+
host,
|
|
257
|
+
token,
|
|
258
|
+
startedAt,
|
|
259
|
+
version: _pkgVersion,
|
|
260
|
+
};
|
|
261
|
+
await mkdir(join(root, OPENLORE_DIR), { recursive: true });
|
|
262
|
+
await writeFile(serveFilePath(root), JSON.stringify(descriptor, null, 2) + '\n', 'utf-8');
|
|
263
|
+
logger.success(`openlore serve listening on http://${host}:${boundPort} (preset: ${presetName})`);
|
|
264
|
+
logger.discovery(`Discovery file: ${serveFilePath(root)}`);
|
|
265
|
+
// ── Freshness: watcher (signatures + vector) + debounced call-graph re-analyze ──
|
|
266
|
+
// The watcher keeps signatures/vector fresh between commits and primes the read
|
|
267
|
+
// cache in place. Its onBatchFlushed hook schedules a heavier full re-analyze so
|
|
268
|
+
// the CALL GRAPH (which the watcher deliberately skips) also stays fresh between
|
|
269
|
+
// commits — turning divergence from "wait for the next commit" into continuous.
|
|
270
|
+
let watcher;
|
|
271
|
+
let reanalyzeTimer;
|
|
272
|
+
let reanalyzeRunning = false;
|
|
273
|
+
let reanalyzePending = false;
|
|
274
|
+
const runReanalyze = async () => {
|
|
275
|
+
if (reanalyzeRunning) {
|
|
276
|
+
reanalyzePending = true;
|
|
277
|
+
return;
|
|
278
|
+
} // single-flight + coalesce
|
|
279
|
+
reanalyzeRunning = true;
|
|
280
|
+
try {
|
|
281
|
+
await openloreAnalyze({ rootPath: root, force: true });
|
|
282
|
+
logger.discovery(`[serve] call graph re-analyzed (${root})`);
|
|
283
|
+
}
|
|
284
|
+
catch (err) {
|
|
285
|
+
logger.warning(`[serve] re-analyze failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
286
|
+
}
|
|
287
|
+
finally {
|
|
288
|
+
reanalyzeRunning = false;
|
|
289
|
+
if (reanalyzePending) {
|
|
290
|
+
reanalyzePending = false;
|
|
291
|
+
scheduleReanalyze();
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
function scheduleReanalyze() {
|
|
296
|
+
if (reanalyzeTimer)
|
|
297
|
+
clearTimeout(reanalyzeTimer);
|
|
298
|
+
reanalyzeTimer = setTimeout(() => void runReanalyze(), REANALYZE_DEBOUNCE_MS);
|
|
299
|
+
}
|
|
300
|
+
if (options.watch !== false) {
|
|
301
|
+
watcher = new McpWatcher({ rootPath: root, onBatchFlushed: () => scheduleReanalyze() });
|
|
302
|
+
try {
|
|
303
|
+
await watcher.start();
|
|
304
|
+
logger.discovery(`[serve] watching ${root} — signatures/vector live, call-graph re-analyze debounced`);
|
|
305
|
+
}
|
|
306
|
+
catch (err) {
|
|
307
|
+
logger.warning(`[serve] watcher failed to start: ${err instanceof Error ? err.message : String(err)}`);
|
|
308
|
+
watcher = undefined;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
// Clean shutdown: drop the descriptor so clients don't reuse a dead port.
|
|
312
|
+
// Signal handlers exit the process; the returned close() is for in-process
|
|
313
|
+
// callers (tests) that must not kill the host.
|
|
314
|
+
let shuttingDown = false;
|
|
315
|
+
const teardown = async () => {
|
|
316
|
+
if (shuttingDown)
|
|
317
|
+
return;
|
|
318
|
+
shuttingDown = true;
|
|
319
|
+
if (reanalyzeTimer)
|
|
320
|
+
clearTimeout(reanalyzeTimer);
|
|
321
|
+
if (watcher)
|
|
322
|
+
await watcher.stop().catch(() => { });
|
|
323
|
+
await unlink(serveFilePath(root)).catch(() => { });
|
|
324
|
+
await new Promise((res) => server.close(() => res()));
|
|
325
|
+
};
|
|
326
|
+
const exitAfterTeardown = async () => {
|
|
327
|
+
await teardown();
|
|
328
|
+
process.exit(0);
|
|
329
|
+
};
|
|
330
|
+
process.on('SIGINT', () => void exitAfterTeardown());
|
|
331
|
+
process.on('SIGTERM', () => void exitAfterTeardown());
|
|
332
|
+
return {
|
|
333
|
+
port: boundPort,
|
|
334
|
+
host,
|
|
335
|
+
token,
|
|
336
|
+
baseUrl: `http://${host}:${boundPort}`,
|
|
337
|
+
close: teardown,
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
export const serveCommand = new Command('serve')
|
|
341
|
+
.description('Start a warm local HTTP daemon exposing openlore tools (loopback, for editor/agent integrations like Pi)')
|
|
342
|
+
.option('-d, --directory <path>', 'Project root to serve (discovery file written here)', process.cwd())
|
|
343
|
+
.option('-p, --port <number>', 'Port to bind (default: ephemeral free port)')
|
|
344
|
+
.option('--host <host>', 'Host to bind', '127.0.0.1')
|
|
345
|
+
.option('--preset <name>', 'Advisory tool surface reported by /health (minimal, navigation, or all). The daemon still ' +
|
|
346
|
+
'dispatches any known tool; clients curate their own surface. Default: navigation', 'navigation')
|
|
347
|
+
.option('--token <token>', 'Require this token as the x-openlore-token header (default: $OPENLORE_SERVE_TOKEN)')
|
|
348
|
+
.option('--no-watch', 'Disable the freshness watcher + debounced call-graph re-analyze')
|
|
349
|
+
.option('--stop', 'Stop a running daemon for --directory and exit')
|
|
350
|
+
.addHelpText('after', `
|
|
351
|
+
Examples:
|
|
352
|
+
$ openlore serve Warm daemon, navigation preset, ephemeral port
|
|
353
|
+
$ openlore serve --preset all --port 7077 All tools on a fixed port
|
|
354
|
+
$ openlore serve --stop Stop the daemon serving this directory
|
|
355
|
+
|
|
356
|
+
$ curl 127.0.0.1:$PORT/health
|
|
357
|
+
$ curl -XPOST 127.0.0.1:$PORT/tool/orient -d '{"args":{"task":"add rate limiting"}}'
|
|
358
|
+
`)
|
|
359
|
+
.action(async (options) => {
|
|
360
|
+
await startServe(options);
|
|
361
|
+
});
|
|
362
|
+
//# sourceMappingURL=serve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serve.js","sourceRoot":"","sources":["../../../src/cli/commands/serve.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/setup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/setup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAqQpC,eAAO,MAAM,YAAY,SAgLrB,CAAC"}
|
|
@@ -88,7 +88,7 @@ async function copyFile(src, dest, force) {
|
|
|
88
88
|
// ============================================================================
|
|
89
89
|
// SKILL MANIFESTS
|
|
90
90
|
// ============================================================================
|
|
91
|
-
function buildManifest(projectRoot) {
|
|
91
|
+
function buildManifest(projectRoot, piGlobal = false) {
|
|
92
92
|
const ex = join(PACKAGE_ROOT, 'examples');
|
|
93
93
|
const VIBE_SKILLS = [
|
|
94
94
|
'openlore-analyze-codebase',
|
|
@@ -182,13 +182,23 @@ function buildManifest(projectRoot) {
|
|
|
182
182
|
dest: join(projectRoot, '.opencode', 'prompts', 'sisyphus-sdd.md'),
|
|
183
183
|
},
|
|
184
184
|
],
|
|
185
|
+
// Pi (pi.dev) — a single TS extension, not per-skill markdown. Project-local
|
|
186
|
+
// by default; --global installs it for every project.
|
|
187
|
+
pi: [
|
|
188
|
+
{
|
|
189
|
+
src: join(ex, 'pi', 'openlore.ts'),
|
|
190
|
+
dest: piGlobal
|
|
191
|
+
? join(homedir(), '.pi', 'agent', 'extensions', 'openlore.ts')
|
|
192
|
+
: join(projectRoot, '.pi', 'extensions', 'openlore.ts'),
|
|
193
|
+
},
|
|
194
|
+
],
|
|
185
195
|
};
|
|
186
196
|
}
|
|
187
197
|
// ============================================================================
|
|
188
198
|
// CORE
|
|
189
199
|
// ============================================================================
|
|
190
|
-
async function runSetup(projectRoot, tools, force) {
|
|
191
|
-
const manifest = buildManifest(projectRoot);
|
|
200
|
+
async function runSetup(projectRoot, tools, force, piGlobal = false) {
|
|
201
|
+
const manifest = buildManifest(projectRoot, piGlobal);
|
|
192
202
|
const results = [];
|
|
193
203
|
for (const tool of tools) {
|
|
194
204
|
for (const entry of manifest[tool]) {
|
|
@@ -211,17 +221,18 @@ async function runSetup(projectRoot, tools, force) {
|
|
|
211
221
|
export const setupCommand = new Command('setup')
|
|
212
222
|
.description('Install workflow skills and agent integration files into this project.\n' +
|
|
213
223
|
'Copies static assets from the openlore package — safe to re-run (skips existing files).')
|
|
214
|
-
.option('--tools <list>', 'Comma-separated list of tools to install: vibe, cline, claude, opencode, gsd, bmad (default: all)')
|
|
224
|
+
.option('--tools <list>', 'Comma-separated list of tools to install: vibe, cline, claude, opencode, gsd, bmad, pi (default: all)')
|
|
215
225
|
.option('--force', 'Overwrite existing files (use after upgrading openlore to pull in updated skills)', false)
|
|
216
226
|
.option('--dir <path>', 'Project root directory', process.cwd())
|
|
227
|
+
.option('--global', 'For the pi target: install the extension to ~/.pi/agent/extensions/ instead of the project', false)
|
|
217
228
|
.action(async (options) => {
|
|
218
229
|
const projectRoot = options.dir;
|
|
219
|
-
const allTools = ['vibe', 'cline', 'gsd', 'bmad', 'claude', 'opencode', 'omoa'];
|
|
230
|
+
const allTools = ['vibe', 'cline', 'gsd', 'bmad', 'claude', 'opencode', 'omoa', 'pi'];
|
|
220
231
|
let tools;
|
|
221
232
|
if (options.tools) {
|
|
222
233
|
tools = options.tools.split(',').map((t) => t.trim()).filter((t) => allTools.includes(t));
|
|
223
234
|
if (tools.length === 0) {
|
|
224
|
-
logger.error('setup: no valid tools specified. Valid values: vibe, cline, gsd, bmad, claude, opencode, omoa');
|
|
235
|
+
logger.error('setup: no valid tools specified. Valid values: vibe, cline, gsd, bmad, claude, opencode, omoa, pi');
|
|
225
236
|
process.exit(1);
|
|
226
237
|
}
|
|
227
238
|
}
|
|
@@ -262,6 +273,10 @@ export const setupCommand = new Command('setup')
|
|
|
262
273
|
value: 'omoa',
|
|
263
274
|
checked: omoaDetected,
|
|
264
275
|
},
|
|
276
|
+
{
|
|
277
|
+
name: 'Pi (.pi/extensions/openlore.ts — warm-daemon extension; --global for ~/.pi)',
|
|
278
|
+
value: 'pi',
|
|
279
|
+
},
|
|
265
280
|
],
|
|
266
281
|
});
|
|
267
282
|
if (selected.length === 0) {
|
|
@@ -279,7 +294,7 @@ export const setupCommand = new Command('setup')
|
|
|
279
294
|
logger.success(`Installing workflow skills into ${projectRoot}`);
|
|
280
295
|
let results;
|
|
281
296
|
try {
|
|
282
|
-
results = await runSetup(projectRoot, tools, options.force);
|
|
297
|
+
results = await runSetup(projectRoot, tools, options.force, options.global);
|
|
283
298
|
}
|
|
284
299
|
catch (err) {
|
|
285
300
|
logger.error(`setup failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -305,6 +320,7 @@ export const setupCommand = new Command('setup')
|
|
|
305
320
|
gsd: 'get-shit-done (GSD)',
|
|
306
321
|
bmad: 'BMAD',
|
|
307
322
|
omoa: 'oh-my-openagent (SDD plugins)',
|
|
323
|
+
pi: 'Pi (pi.dev)',
|
|
308
324
|
};
|
|
309
325
|
for (const tool of tools) {
|
|
310
326
|
const entries = byTool[tool] ?? [];
|