recallx 1.0.2 → 1.0.4
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 +20 -12
- package/app/cli/src/cli.js +17 -1
- package/app/cli/src/format.js +30 -0
- package/app/cli/src/update.js +118 -0
- package/app/server/app.js +3 -4
- package/app/shared/version.js +1 -1
- package/dist/renderer/assets/{ProjectGraphCanvas-BVlOgk7l.js → ProjectGraphCanvas-CQV7FYSd.js} +1 -1
- package/dist/renderer/assets/index-BM5Zf6Wz.js +69 -0
- package/dist/renderer/index.html +1 -1
- package/package.json +1 -1
- package/dist/renderer/assets/index-JtUcLLhP.js +0 -76
package/README.md
CHANGED
|
@@ -35,13 +35,14 @@ RecallX is documented around three public ways to use it:
|
|
|
35
35
|
2. npm package `recallx` for the full local runtime
|
|
36
36
|
3. npm package `recallx-headless` for the headless runtime
|
|
37
37
|
|
|
38
|
+
RecallX does not currently ship separate OS-native installers or package formats such as `.dmg`, `.msi`, `.deb`, or `AppImage`.
|
|
39
|
+
|
|
38
40
|
## 1. Git Public Repo
|
|
39
41
|
|
|
40
42
|
Use the public repo when you want the full source-run surface:
|
|
41
43
|
|
|
42
44
|
- local API under `/api/v1`
|
|
43
45
|
- source-run renderer workflow through `npm run dev`
|
|
44
|
-
- source-run desktop workflow through `npm run dev:desktop`
|
|
45
46
|
- stdio MCP bridge through `npm run mcp`
|
|
46
47
|
- runtime workspace create/open switching without restarting the service
|
|
47
48
|
|
|
@@ -52,12 +53,6 @@ npm install
|
|
|
52
53
|
npm run dev
|
|
53
54
|
```
|
|
54
55
|
|
|
55
|
-
Desktop runtime from source:
|
|
56
|
-
|
|
57
|
-
```bash
|
|
58
|
-
npm run dev:desktop
|
|
59
|
-
```
|
|
60
|
-
|
|
61
56
|
MCP from source:
|
|
62
57
|
|
|
63
58
|
```bash
|
|
@@ -94,6 +89,7 @@ In another shell:
|
|
|
94
89
|
|
|
95
90
|
```bash
|
|
96
91
|
recallx health
|
|
92
|
+
recallx update
|
|
97
93
|
recallx mcp install
|
|
98
94
|
recallx-mcp --help
|
|
99
95
|
```
|
|
@@ -106,10 +102,6 @@ The full npm package includes:
|
|
|
106
102
|
- `recallx serve` and subcommands
|
|
107
103
|
- `recallx-mcp`
|
|
108
104
|
|
|
109
|
-
The full npm package does not include:
|
|
110
|
-
|
|
111
|
-
- desktop release artifacts
|
|
112
|
-
|
|
113
105
|
`recallx mcp install` writes a stable launcher to `~/.recallx/bin/recallx-mcp`, which is the recommended command path for Codex and other editor MCP configs.
|
|
114
106
|
|
|
115
107
|
If the API is running in bearer mode, set `RECALLX_API_TOKEN` in the MCP client environment. The launcher does not write tokens to disk.
|
|
@@ -128,6 +120,15 @@ recallx serve --workspace-root /Users/name/Documents/RecallX
|
|
|
128
120
|
recallx serve --api-token secret-token
|
|
129
121
|
```
|
|
130
122
|
|
|
123
|
+
To update an npm-installed full runtime:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
recallx update
|
|
127
|
+
recallx update --apply
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
`recallx update` currently supports npm global installs of `recallx` and `recallx-headless`. Source checkouts should keep using their package manager directly.
|
|
131
|
+
|
|
131
132
|
## 3. npm Headless Runtime (`recallx-headless`)
|
|
132
133
|
|
|
133
134
|
Use the headless npm package when you want the local API, CLI, and MCP entrypoint without shipping the renderer bundle:
|
|
@@ -141,6 +142,7 @@ In another shell:
|
|
|
141
142
|
|
|
142
143
|
```bash
|
|
143
144
|
recallx health
|
|
145
|
+
recallx update
|
|
144
146
|
recallx-mcp --help
|
|
145
147
|
```
|
|
146
148
|
|
|
@@ -154,10 +156,16 @@ The headless npm package includes:
|
|
|
154
156
|
The headless npm package does not include:
|
|
155
157
|
|
|
156
158
|
- renderer pages
|
|
157
|
-
- desktop release artifacts
|
|
158
159
|
|
|
159
160
|
At `/`, the headless runtime returns a small runtime notice instead of the renderer.
|
|
160
161
|
|
|
162
|
+
To update an npm-installed headless runtime:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
recallx update
|
|
166
|
+
recallx update --apply
|
|
167
|
+
```
|
|
168
|
+
|
|
161
169
|
Node requirements:
|
|
162
170
|
|
|
163
171
|
- npm packages: Node 22.13+
|
package/app/cli/src/cli.js
CHANGED
|
@@ -5,7 +5,8 @@ import path from "node:path";
|
|
|
5
5
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
6
6
|
import { getApiBase, getAuthToken, requestJson } from "./http.js";
|
|
7
7
|
import { RECALLX_VERSION } from "../../shared/version.js";
|
|
8
|
-
import {
|
|
8
|
+
import { applyCliUpdate, getCliUpdatePlan } from "./update.js";
|
|
9
|
+
import { renderActivitySearchResults, renderBundleMarkdown, renderGovernanceIssues, renderJson, renderNode, renderRelated, renderSearchResults, renderTelemetryErrors, renderTelemetrySummary, renderText, renderUpdateResult, renderWorkspaceSearchResults, renderWorkspaces, } from "./format.js";
|
|
9
10
|
const DEFAULT_SOURCE = {
|
|
10
11
|
actorType: "human",
|
|
11
12
|
actorLabel: "recallx-cli",
|
|
@@ -27,6 +28,8 @@ export async function runCli(argv) {
|
|
|
27
28
|
return runHealth(apiBase, token, format);
|
|
28
29
|
case "serve":
|
|
29
30
|
return runServe(args);
|
|
31
|
+
case "update":
|
|
32
|
+
return runUpdate(format, args);
|
|
30
33
|
case "mcp":
|
|
31
34
|
return runMcp(apiBase, token, format, args, positionals);
|
|
32
35
|
case "search":
|
|
@@ -85,6 +88,15 @@ async function runServe(args) {
|
|
|
85
88
|
}
|
|
86
89
|
await import(pathToFileURL(resolveServerEntryScript()).href);
|
|
87
90
|
}
|
|
91
|
+
export async function runUpdate(format, args, dependencies = {}) {
|
|
92
|
+
const plan = await (dependencies.getCliUpdatePlan ?? getCliUpdatePlan)();
|
|
93
|
+
if (parseBooleanFlag(args.apply, false)) {
|
|
94
|
+
const result = await (dependencies.applyCliUpdate ?? applyCliUpdate)(plan);
|
|
95
|
+
outputData(result, format, "update");
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
outputData(plan, format, "update");
|
|
99
|
+
}
|
|
88
100
|
async function runMcp(apiBase, token, format, args, positionals) {
|
|
89
101
|
const action = positionals[0] || args.action || "config";
|
|
90
102
|
const launcherPath = args.path || args.launcher || DEFAULT_MCP_LAUNCHER_PATH;
|
|
@@ -582,6 +594,9 @@ function outputData(data, format, command) {
|
|
|
582
594
|
"",
|
|
583
595
|
].join("\n"));
|
|
584
596
|
return;
|
|
597
|
+
case "update":
|
|
598
|
+
writeStdout(renderUpdateResult(payload));
|
|
599
|
+
return;
|
|
585
600
|
default:
|
|
586
601
|
writeStdout(renderText(payload));
|
|
587
602
|
}
|
|
@@ -657,6 +672,7 @@ Usage:
|
|
|
657
672
|
recallx serve [--workspace-name Personal] [--api-token secret]
|
|
658
673
|
recallx serve [--renderer-dist /path/to/dist/renderer]
|
|
659
674
|
recallx health
|
|
675
|
+
recallx update [--apply]
|
|
660
676
|
recallx mcp config
|
|
661
677
|
recallx mcp install [--path ~/.recallx/bin/recallx-mcp]
|
|
662
678
|
recallx mcp path
|
package/app/cli/src/format.js
CHANGED
|
@@ -144,6 +144,36 @@ export function renderWorkspaces(data) {
|
|
|
144
144
|
.join("\n\n")}\n`;
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
+
export function renderUpdateResult(data) {
|
|
148
|
+
const lines = [
|
|
149
|
+
`package: ${data?.packageName || ""}`,
|
|
150
|
+
`current: ${data?.currentVersion || ""}`,
|
|
151
|
+
`latest: ${data?.latestVersion || ""}`,
|
|
152
|
+
];
|
|
153
|
+
|
|
154
|
+
if (data?.status === "updated") {
|
|
155
|
+
lines.push("status: updated");
|
|
156
|
+
} else if (data?.status === "up_to_date") {
|
|
157
|
+
lines.push("status: up to date");
|
|
158
|
+
} else {
|
|
159
|
+
lines.push("status: update available");
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (data?.installCommand) {
|
|
163
|
+
lines.push(`command: ${data.installCommand}`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (data?.status === "update_available" && data?.applied !== true) {
|
|
167
|
+
lines.push("hint: re-run with `recallx update --apply` to install the latest npm package from this shell.");
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (data?.packageRoot) {
|
|
171
|
+
lines.push(`packageRoot: ${data.packageRoot}`);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return `${lines.join("\n")}\n`;
|
|
175
|
+
}
|
|
176
|
+
|
|
147
177
|
export function renderBundleMarkdown(bundle) {
|
|
148
178
|
const lines = [];
|
|
149
179
|
lines.push(`# ${bundle.target?.title || bundle.target?.id || "Context bundle"}`);
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { execFile, execFileSync } from "node:child_process";
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { promisify } from "node:util";
|
|
6
|
+
const execFileAsync = promisify(execFile);
|
|
7
|
+
const SUPPORTED_NPM_PACKAGES = new Set(["recallx", "recallx-headless"]);
|
|
8
|
+
export async function getCliUpdatePlan(options = {}) {
|
|
9
|
+
const moduleUrl = options.moduleUrl ?? import.meta.url;
|
|
10
|
+
const packageRoot = options.packageRoot ?? resolveCliPackageRoot(moduleUrl);
|
|
11
|
+
const packageJson = readPackageJson(packageRoot, options.readFileSyncFn ?? readFileSync);
|
|
12
|
+
const packageName = options.packageName ?? packageJson.name;
|
|
13
|
+
const currentVersion = options.currentVersion ?? packageJson.version;
|
|
14
|
+
const platform = options.platform ?? process.platform;
|
|
15
|
+
const npmCommand = options.npmCommand ?? resolveNpmCommand(platform);
|
|
16
|
+
const execAsync = options.execFileAsyncFn ?? execFileAsync;
|
|
17
|
+
if (!SUPPORTED_NPM_PACKAGES.has(packageName)) {
|
|
18
|
+
throw new Error("UPDATE_UNSUPPORTED: `recallx update` currently supports npm-installed `recallx` and `recallx-headless` runtimes only.");
|
|
19
|
+
}
|
|
20
|
+
const globalRoot = (await runCommand(execAsync, npmCommand, ["root", "-g"])).trim();
|
|
21
|
+
if (!globalRoot || !isPathInside(packageRoot, globalRoot)) {
|
|
22
|
+
throw new Error("UPDATE_UNSUPPORTED: `recallx update` only works for npm global installs. For source checkouts or other install methods, update the package with your package manager directly.");
|
|
23
|
+
}
|
|
24
|
+
const latestVersionRaw = await runCommand(execAsync, npmCommand, ["view", packageName, "version", "--json"]);
|
|
25
|
+
const latestVersion = normalizeVersionPayload(latestVersionRaw);
|
|
26
|
+
const installArgs = ["install", "-g", `${packageName}@latest`];
|
|
27
|
+
return {
|
|
28
|
+
packageName,
|
|
29
|
+
currentVersion,
|
|
30
|
+
latestVersion,
|
|
31
|
+
packageRoot,
|
|
32
|
+
globalRoot,
|
|
33
|
+
npmCommand,
|
|
34
|
+
installArgs,
|
|
35
|
+
installCommand: [npmCommand, ...installArgs].map(quoteShellArg).join(" "),
|
|
36
|
+
status: currentVersion === latestVersion ? "up_to_date" : "update_available",
|
|
37
|
+
applied: false,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export function applyCliUpdate(plan, options = {}) {
|
|
41
|
+
if (!plan || typeof plan !== "object") {
|
|
42
|
+
throw new Error("UPDATE_INVALID_PLAN: Missing update plan.");
|
|
43
|
+
}
|
|
44
|
+
if (plan.status === "up_to_date") {
|
|
45
|
+
return {
|
|
46
|
+
...plan,
|
|
47
|
+
applied: false,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
const platform = options.platform ?? process.platform;
|
|
51
|
+
const npmCommand = options.npmCommand ?? plan.npmCommand ?? resolveNpmCommand(platform);
|
|
52
|
+
const execSync = options.execFileSyncFn ?? execFileSync;
|
|
53
|
+
execSync(npmCommand, plan.installArgs ?? ["install", "-g", `${plan.packageName}@latest`], {
|
|
54
|
+
stdio: "inherit",
|
|
55
|
+
});
|
|
56
|
+
return {
|
|
57
|
+
...plan,
|
|
58
|
+
status: "updated",
|
|
59
|
+
applied: true,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function resolveCliPackageRoot(moduleUrl) {
|
|
63
|
+
const modulePath = fileURLToPath(moduleUrl);
|
|
64
|
+
return path.resolve(path.dirname(modulePath), "../../..");
|
|
65
|
+
}
|
|
66
|
+
function readPackageJson(packageRoot, readFileSyncFn) {
|
|
67
|
+
const packageJsonPath = path.join(packageRoot, "package.json");
|
|
68
|
+
if (!existsSync(packageJsonPath)) {
|
|
69
|
+
throw new Error(`UPDATE_UNSUPPORTED: Could not find package metadata at ${packageJsonPath}.`);
|
|
70
|
+
}
|
|
71
|
+
return JSON.parse(readFileSyncFn(packageJsonPath, "utf8"));
|
|
72
|
+
}
|
|
73
|
+
async function runCommand(execAsync, command, args) {
|
|
74
|
+
try {
|
|
75
|
+
const result = await execAsync(command, args, { encoding: "utf8" });
|
|
76
|
+
return typeof result.stdout === "string" ? result.stdout.trim() : "";
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
const stderr = error && typeof error === "object" && "stderr" in error && typeof error.stderr === "string"
|
|
80
|
+
? error.stderr.trim()
|
|
81
|
+
: "";
|
|
82
|
+
const detail = stderr || (error instanceof Error ? error.message : String(error));
|
|
83
|
+
throw new Error(`UPDATE_COMMAND_FAILED: ${detail}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function normalizeVersionPayload(value) {
|
|
87
|
+
if (!value) {
|
|
88
|
+
throw new Error("UPDATE_LOOKUP_FAILED: npm did not return a version.");
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
const parsed = JSON.parse(value);
|
|
92
|
+
if (Array.isArray(parsed)) {
|
|
93
|
+
const last = parsed.at(-1);
|
|
94
|
+
if (typeof last === "string" && last.trim()) {
|
|
95
|
+
return last;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (typeof parsed === "string" && parsed.trim()) {
|
|
99
|
+
return parsed;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
if (typeof value === "string" && value.trim()) {
|
|
104
|
+
return value.trim();
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
throw new Error("UPDATE_LOOKUP_FAILED: npm returned an invalid version payload.");
|
|
108
|
+
}
|
|
109
|
+
function isPathInside(candidatePath, parentPath) {
|
|
110
|
+
const relative = path.relative(parentPath, candidatePath);
|
|
111
|
+
return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
|
|
112
|
+
}
|
|
113
|
+
function quoteShellArg(value) {
|
|
114
|
+
return `"${String(value).replace(/"/g, '\\"')}"`;
|
|
115
|
+
}
|
|
116
|
+
function resolveNpmCommand(platform) {
|
|
117
|
+
return platform === "win32" ? "npm.cmd" : "npm";
|
|
118
|
+
}
|
package/app/server/app.js
CHANGED
|
@@ -14,7 +14,6 @@ import { buildProjectGraph } from "./project-graph.js";
|
|
|
14
14
|
import { createId, isPathWithinRoot } from "./utils.js";
|
|
15
15
|
const relationTypeSet = new Set(relationTypes);
|
|
16
16
|
const allowedLoopbackHostnames = new Set(["127.0.0.1", "localhost", "::1", "[::1]"]);
|
|
17
|
-
const isDesktopManagedApi = process.env.ELECTRON_RUN_AS_NODE === "1";
|
|
18
17
|
const updateNodeRequestSchema = updateNodeSchema.extend({
|
|
19
18
|
source: sourceSchema
|
|
20
19
|
});
|
|
@@ -453,7 +452,7 @@ function buildServiceIndex(workspaceInfo) {
|
|
|
453
452
|
{
|
|
454
453
|
method: "GET",
|
|
455
454
|
path: "/api/v1/observability/errors?since=24h&surface=mcp",
|
|
456
|
-
purpose: "Inspect recent telemetry errors for the API
|
|
455
|
+
purpose: "Inspect recent telemetry errors for the API or MCP bridge."
|
|
457
456
|
},
|
|
458
457
|
{
|
|
459
458
|
method: "POST",
|
|
@@ -543,7 +542,7 @@ export function createRecallXApp(params) {
|
|
|
543
542
|
"observability.capturePayloadShape"
|
|
544
543
|
]);
|
|
545
544
|
return {
|
|
546
|
-
enabled:
|
|
545
|
+
enabled: true,
|
|
547
546
|
workspaceRoot: currentWorkspaceRoot(),
|
|
548
547
|
workspaceName: currentWorkspaceInfo().workspaceName,
|
|
549
548
|
retentionDays: Math.max(1, parseNumberSetting(settings["observability.retentionDays"], 14)),
|
|
@@ -1391,7 +1390,7 @@ export function createRecallXApp(params) {
|
|
|
1391
1390
|
}));
|
|
1392
1391
|
app.get("/api/v1/observability/errors", handleAsyncRoute(async (request, response) => {
|
|
1393
1392
|
const surface = readRequestParam(request.query.surface);
|
|
1394
|
-
const normalizedSurface = surface === "api" || surface === "mcp"
|
|
1393
|
+
const normalizedSurface = surface === "api" || surface === "mcp" ? surface : "all";
|
|
1395
1394
|
const errors = await observability.listErrors({
|
|
1396
1395
|
since: readRequestParam(request.query.since),
|
|
1397
1396
|
surface: normalizedSurface,
|
package/app/shared/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const RECALLX_VERSION = "1.0.
|
|
1
|
+
export const RECALLX_VERSION = "1.0.4";
|