memorix 1.6.1 → 1.6.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/CHANGELOG.md +22 -0
- package/TEAM.md +86 -86
- package/dist/cli/index.js +1574 -1063
- package/dist/index.js +588 -107
- package/dist/maintenance-runner.js +565 -84
- package/dist/media-audio-runner.js +565 -84
- package/dist/media-video-runner.js +565 -84
- package/dist/memcode-runtime/CHANGELOG.md +22 -0
- package/dist/sdk.js +621 -111
- package/dist/vector-backfill-runner.js +565 -84
- package/docs/DESIGN_DECISIONS.md +357 -357
- package/docs/PERFORMANCE.md +15 -0
- package/package.json +2 -1
- package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
- package/plugins/codebuddy/memorix/.codebuddy-plugin/plugin.json +1 -1
- package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
- package/plugins/copilot/memorix/plugin.json +1 -1
- package/plugins/gemini/memorix/gemini-extension.json +1 -1
- package/plugins/omp/memorix/package.json +1 -1
- package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
- package/plugins/pi/memorix/package.json +1 -1
- package/server.json +2 -2
- package/src/audit/index.ts +156 -156
- package/src/cli/commands/background.ts +34 -2
- package/src/cli/commands/formation.ts +48 -48
- package/src/cli/commands/git-hook-install.ts +111 -111
- package/src/cli/commands/handoff.ts +54 -54
- package/src/cli/commands/hooks-status.ts +63 -63
- package/src/cli/commands/ingest.ts +44 -44
- package/src/cli/commands/integrate-shared.ts +15 -15
- package/src/cli/commands/lock.ts +82 -82
- package/src/cli/commands/message.ts +104 -104
- package/src/cli/commands/operator-shared.ts +6 -3
- package/src/cli/commands/poll.ts +58 -58
- package/src/cli/commands/reasoning.ts +118 -118
- package/src/cli/commands/serve-http.ts +4 -0
- package/src/cli/commands/skills.ts +114 -114
- package/src/cli/commands/task.ts +167 -167
- package/src/cli/commands/transfer.ts +47 -47
- package/src/cli/index.ts +406 -406
- package/src/cli/tui/ChatView.tsx +234 -234
- package/src/cli/tui/CommandBar.tsx +312 -312
- package/src/cli/tui/ContextRail.tsx +118 -118
- package/src/cli/tui/HeaderBar.tsx +72 -72
- package/src/cli/tui/LogoBanner.tsx +51 -51
- package/src/cli/tui/index.ts +41 -41
- package/src/cli/version.ts +7 -7
- package/src/cli/workbench.ts +1 -1
- package/src/dashboard/project-classification.ts +64 -64
- package/src/embedding/api-provider.ts +503 -27
- package/src/embedding/provider.ts +20 -3
- package/src/git/hooks-path.ts +85 -85
- package/src/hooks/lifecycle.ts +1 -0
- package/src/hooks/pattern-detector.ts +173 -173
- package/src/hooks/significance-filter.ts +250 -250
- package/src/llm/memory-manager.ts +328 -328
- package/src/llm/quality.ts +248 -248
- package/src/memory/attribution-guard.ts +249 -249
- package/src/memory/disclosure-policy.ts +135 -135
- package/src/memory/entity-extractor.ts +197 -197
- package/src/memory/formation/evaluate.ts +217 -217
- package/src/memory/freshness.ts +122 -122
- package/src/memory/graph.ts +197 -197
- package/src/memory/observations.ts +68 -18
- package/src/memory/refs.ts +94 -94
- package/src/memory/secret-filter.ts +79 -79
- package/src/orchestrate/adapters/claude-stream.ts +192 -192
- package/src/orchestrate/adapters/claude.ts +111 -111
- package/src/orchestrate/adapters/codex-stream.ts +134 -134
- package/src/orchestrate/adapters/codex.ts +41 -41
- package/src/orchestrate/adapters/gemini-stream.ts +166 -166
- package/src/orchestrate/adapters/gemini.ts +42 -42
- package/src/orchestrate/adapters/index.ts +73 -73
- package/src/orchestrate/adapters/opencode-stream.ts +143 -143
- package/src/orchestrate/adapters/opencode.ts +47 -47
- package/src/orchestrate/adapters/types.ts +77 -77
- package/src/orchestrate/capability-router.ts +284 -284
- package/src/orchestrate/cost-tracker.ts +219 -219
- package/src/orchestrate/error-recovery.ts +191 -191
- package/src/orchestrate/evidence.ts +140 -140
- package/src/orchestrate/ledger.ts +110 -110
- package/src/orchestrate/memorix-bridge.ts +343 -343
- package/src/orchestrate/output-budget.ts +80 -80
- package/src/orchestrate/permission.ts +152 -152
- package/src/orchestrate/pipeline-trace.ts +131 -131
- package/src/orchestrate/prompt-builder.ts +155 -155
- package/src/orchestrate/ring-buffer.ts +37 -37
- package/src/orchestrate/task-graph.ts +389 -389
- package/src/project/aliases.ts +374 -374
- package/src/rules/adapters/trae.ts +56 -56
- package/src/rules/syncer.ts +235 -235
- package/src/sdk.ts +12 -8
- package/src/search/query-expansion.ts +52 -52
- package/src/server/formation-timeout.ts +27 -27
- package/src/skills/mini-skills.ts +386 -386
- package/src/store/chat-store.ts +119 -119
- package/src/store/graph-store.ts +249 -249
- package/src/store/mini-skill-store.ts +349 -349
- package/src/store/orama-store.ts +21 -3
- package/src/store/persistence-json.ts +212 -212
- package/src/store/persistence.ts +291 -291
- package/src/store/project-affinity.ts +195 -195
- package/src/team/event-bus.ts +76 -76
- package/src/team/handoff.ts +161 -161
- package/src/team/poll.ts +132 -132
- package/src/workspace/mcp-adapters/codex.ts +191 -191
- package/src/workspace/mcp-adapters/copilot.ts +105 -105
- package/src/workspace/mcp-adapters/cursor.ts +53 -53
- package/src/workspace/mcp-adapters/kiro.ts +64 -64
- package/src/workspace/mcp-adapters/opencode.ts +123 -123
- package/src/workspace/mcp-adapters/trae.ts +134 -134
- package/src/workspace/mcp-adapters/windsurf.ts +91 -91
- package/src/workspace/sanitizer.ts +60 -60
package/docs/PERFORMANCE.md
CHANGED
|
@@ -78,6 +78,7 @@ On the release development machine used for this check, the healthy HTTP service
|
|
|
78
78
|
| `MEMORIX_RERANK_TIMEOUT_MS` | provider default | Bound slow LLM rerank calls |
|
|
79
79
|
| `memorix memory search --quality fast` | n/a | Force a fully local retrieval path for a latency-sensitive call |
|
|
80
80
|
| `npm run benchmark:retrieval -- --records 1000 --runs 100` | n/a | Reproduce hot in-process lexical retrieval latency; not an end-to-end claim |
|
|
81
|
+
| `npm run gate:large-store -- --records 40000` | n/a | Exercise SDK, HTTP MCP, hook persistence, cache integrity, and reopen behavior against a large isolated store |
|
|
81
82
|
| `[codegraph].max_file_bytes` | `2097152` | Raise only when a large file is intentional source that should enter Code Memory |
|
|
82
83
|
| `memorix retention status` | report only | Inspect whether memory growth needs cleanup |
|
|
83
84
|
| `memorix retention archive` | explicit | Archive expired memories when the project gets noisy |
|
|
@@ -96,6 +97,20 @@ On the release development machine used for this check, the healthy HTTP service
|
|
|
96
97
|
`/api/maintenance` on that local dashboard before assuming a Code Memory scan
|
|
97
98
|
or lifecycle task completed.
|
|
98
99
|
|
|
100
|
+
## Large-Store Release Gate
|
|
101
|
+
|
|
102
|
+
`npm run gate:large-store -- --records 40000` builds Memorix and creates a
|
|
103
|
+
temporary Git project with an isolated data directory. It runs with embeddings
|
|
104
|
+
disabled, so it does not use provider credentials or make paid network calls.
|
|
105
|
+
The gate measures steady-state writes, first lexical search, process memory,
|
|
106
|
+
HTTP MCP and hook persistence, and an SDK close/reopen cycle. It also verifies
|
|
107
|
+
that the personal hook candidate remains durable on disk without becoming
|
|
108
|
+
visible to an unbound SDK reader.
|
|
109
|
+
|
|
110
|
+
Results are machine-specific and should be compared on the same OS and Node
|
|
111
|
+
version. The release gate is an integrity and regression check, not a public
|
|
112
|
+
latency guarantee.
|
|
113
|
+
|
|
99
114
|
## Current Optimization Opportunities
|
|
100
115
|
|
|
101
116
|
These are not release blockers, but they are reasonable future improvements:
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "memorix",
|
|
3
3
|
"mcpName": "io.github.AVIDS2/memorix",
|
|
4
|
-
"version": "1.6.
|
|
4
|
+
"version": "1.6.2",
|
|
5
5
|
"description": "Local-first shared memory layer for AI coding agents across MCP clients, Git history, reasoning context, and project sessions.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"sideEffects": false,
|
|
@@ -67,6 +67,7 @@
|
|
|
67
67
|
"test:e2e": "vitest run tests/e2e/",
|
|
68
68
|
"benchmark:codegraph": "node scripts/benchmark-codegraph-provider.cjs",
|
|
69
69
|
"benchmark:retrieval": "npm run build && node scripts/benchmark-retrieval.mjs",
|
|
70
|
+
"gate:large-store": "npm run build && node scripts/large-store-gate.mjs",
|
|
70
71
|
"generate:star-history": "node scripts/generate-star-history.mjs",
|
|
71
72
|
"sync:mcp-registry": "node scripts/sync-mcp-registry-manifest.mjs",
|
|
72
73
|
"check:mcp-registry": "node scripts/check-mcp-registry-manifest.mjs",
|
package/server.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"url": "https://github.com/AVIDS2/memorix",
|
|
8
8
|
"source": "github"
|
|
9
9
|
},
|
|
10
|
-
"version": "1.6.
|
|
10
|
+
"version": "1.6.2",
|
|
11
11
|
"websiteUrl": "https://github.com/AVIDS2/memorix#readme",
|
|
12
12
|
"icons": [
|
|
13
13
|
{
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
{
|
|
23
23
|
"registryType": "npm",
|
|
24
24
|
"identifier": "memorix",
|
|
25
|
-
"version": "1.6.
|
|
25
|
+
"version": "1.6.2",
|
|
26
26
|
"runtimeHint": "npx",
|
|
27
27
|
"packageArguments": [
|
|
28
28
|
{
|
package/src/audit/index.ts
CHANGED
|
@@ -1,156 +1,156 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Audit Module - Track Memorix-written files
|
|
3
|
-
*
|
|
4
|
-
* Records all files written by Memorix to distinguish them from
|
|
5
|
-
* project-native files. Provides audit trail for cleanup and rollback.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import * as fs from 'node:fs/promises';
|
|
9
|
-
import * as path from 'node:path';
|
|
10
|
-
import { homedir } from 'node:os';
|
|
11
|
-
|
|
12
|
-
function getAuditFilePath(): string {
|
|
13
|
-
return process.env.MEMORIX_AUDIT_FILE || path.join(homedir(), '.memorix', 'audit.json');
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface AuditEntry {
|
|
17
|
-
type: 'hook' | 'rule' | 'other';
|
|
18
|
-
agent?: string;
|
|
19
|
-
path: string;
|
|
20
|
-
createdAt: string;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export interface ProjectAudit {
|
|
24
|
-
projectRoot: string;
|
|
25
|
-
installedAt: string;
|
|
26
|
-
entries: AuditEntry[];
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface AuditData {
|
|
30
|
-
version: string;
|
|
31
|
-
projects: Record<string, ProjectAudit>;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Load audit data from disk.
|
|
36
|
-
*/
|
|
37
|
-
export async function loadAudit(): Promise<AuditData> {
|
|
38
|
-
try {
|
|
39
|
-
const content = await fs.readFile(getAuditFilePath(), 'utf-8');
|
|
40
|
-
return JSON.parse(content) as AuditData;
|
|
41
|
-
} catch {
|
|
42
|
-
// No audit file yet
|
|
43
|
-
return {
|
|
44
|
-
version: '1.0.0',
|
|
45
|
-
projects: {},
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Save audit data to disk.
|
|
52
|
-
*/
|
|
53
|
-
export async function saveAudit(data: AuditData): Promise<void> {
|
|
54
|
-
const auditFile = getAuditFilePath();
|
|
55
|
-
const dir = path.dirname(auditFile);
|
|
56
|
-
await fs.mkdir(dir, { recursive: true });
|
|
57
|
-
await fs.writeFile(auditFile, JSON.stringify(data, null, 2), 'utf-8');
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Get project ID from project root via .git detection.
|
|
62
|
-
*/
|
|
63
|
-
export function getProjectId(projectRoot: string): string {
|
|
64
|
-
try {
|
|
65
|
-
const { detectProject } = require('../project/detector.js');
|
|
66
|
-
const project = detectProject(projectRoot);
|
|
67
|
-
if (project) return project.id;
|
|
68
|
-
} catch { /* fallback below */ }
|
|
69
|
-
const normalized = projectRoot.replace(/\\/g, '/');
|
|
70
|
-
return `untracked/${normalized}`;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Record a file written by Memorix.
|
|
75
|
-
*/
|
|
76
|
-
export async function recordFile(
|
|
77
|
-
projectRoot: string,
|
|
78
|
-
type: AuditEntry['type'],
|
|
79
|
-
filePath: string,
|
|
80
|
-
agent?: string,
|
|
81
|
-
): Promise<void> {
|
|
82
|
-
const data = await loadAudit();
|
|
83
|
-
const projectId = getProjectId(projectRoot);
|
|
84
|
-
|
|
85
|
-
if (!data.projects[projectId]) {
|
|
86
|
-
data.projects[projectId] = {
|
|
87
|
-
projectRoot,
|
|
88
|
-
installedAt: new Date().toISOString(),
|
|
89
|
-
entries: [],
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Check if entry already exists
|
|
94
|
-
const existingIndex = data.projects[projectId].entries.findIndex(
|
|
95
|
-
(e) => e.path === filePath
|
|
96
|
-
);
|
|
97
|
-
|
|
98
|
-
if (existingIndex === -1) {
|
|
99
|
-
data.projects[projectId].entries.push({
|
|
100
|
-
type,
|
|
101
|
-
agent,
|
|
102
|
-
path: filePath,
|
|
103
|
-
createdAt: new Date().toISOString(),
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
await saveAudit(data);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Get all files written by Memorix for a project.
|
|
112
|
-
*/
|
|
113
|
-
export async function getProjectFiles(projectRoot: string): Promise<AuditEntry[]> {
|
|
114
|
-
const data = await loadAudit();
|
|
115
|
-
const projectId = getProjectId(projectRoot);
|
|
116
|
-
return data.projects[projectId]?.entries || [];
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Remove a file from audit (when uninstalled).
|
|
121
|
-
*/
|
|
122
|
-
export async function removeFile(projectRoot: string, filePath: string): Promise<void> {
|
|
123
|
-
const data = await loadAudit();
|
|
124
|
-
const projectId = getProjectId(projectRoot);
|
|
125
|
-
|
|
126
|
-
if (!data.projects[projectId]) return;
|
|
127
|
-
|
|
128
|
-
data.projects[projectId].entries = data.projects[projectId].entries.filter(
|
|
129
|
-
(e) => e.path !== filePath
|
|
130
|
-
);
|
|
131
|
-
|
|
132
|
-
// If no entries left, remove the project
|
|
133
|
-
if (data.projects[projectId].entries.length === 0) {
|
|
134
|
-
delete data.projects[projectId];
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
await saveAudit(data);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Get all audit entries across all projects.
|
|
142
|
-
*/
|
|
143
|
-
export async function getAllAuditEntries(): Promise<
|
|
144
|
-
Array<{ projectId: string; entry: AuditEntry }>
|
|
145
|
-
> {
|
|
146
|
-
const data = await loadAudit();
|
|
147
|
-
const entries: Array<{ projectId: string; entry: AuditEntry }> = [];
|
|
148
|
-
|
|
149
|
-
for (const [projectId, project] of Object.entries(data.projects)) {
|
|
150
|
-
for (const entry of project.entries) {
|
|
151
|
-
entries.push({ projectId, entry });
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
return entries;
|
|
156
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Audit Module - Track Memorix-written files
|
|
3
|
+
*
|
|
4
|
+
* Records all files written by Memorix to distinguish them from
|
|
5
|
+
* project-native files. Provides audit trail for cleanup and rollback.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import * as fs from 'node:fs/promises';
|
|
9
|
+
import * as path from 'node:path';
|
|
10
|
+
import { homedir } from 'node:os';
|
|
11
|
+
|
|
12
|
+
function getAuditFilePath(): string {
|
|
13
|
+
return process.env.MEMORIX_AUDIT_FILE || path.join(homedir(), '.memorix', 'audit.json');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface AuditEntry {
|
|
17
|
+
type: 'hook' | 'rule' | 'other';
|
|
18
|
+
agent?: string;
|
|
19
|
+
path: string;
|
|
20
|
+
createdAt: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ProjectAudit {
|
|
24
|
+
projectRoot: string;
|
|
25
|
+
installedAt: string;
|
|
26
|
+
entries: AuditEntry[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface AuditData {
|
|
30
|
+
version: string;
|
|
31
|
+
projects: Record<string, ProjectAudit>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Load audit data from disk.
|
|
36
|
+
*/
|
|
37
|
+
export async function loadAudit(): Promise<AuditData> {
|
|
38
|
+
try {
|
|
39
|
+
const content = await fs.readFile(getAuditFilePath(), 'utf-8');
|
|
40
|
+
return JSON.parse(content) as AuditData;
|
|
41
|
+
} catch {
|
|
42
|
+
// No audit file yet
|
|
43
|
+
return {
|
|
44
|
+
version: '1.0.0',
|
|
45
|
+
projects: {},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Save audit data to disk.
|
|
52
|
+
*/
|
|
53
|
+
export async function saveAudit(data: AuditData): Promise<void> {
|
|
54
|
+
const auditFile = getAuditFilePath();
|
|
55
|
+
const dir = path.dirname(auditFile);
|
|
56
|
+
await fs.mkdir(dir, { recursive: true });
|
|
57
|
+
await fs.writeFile(auditFile, JSON.stringify(data, null, 2), 'utf-8');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Get project ID from project root via .git detection.
|
|
62
|
+
*/
|
|
63
|
+
export function getProjectId(projectRoot: string): string {
|
|
64
|
+
try {
|
|
65
|
+
const { detectProject } = require('../project/detector.js');
|
|
66
|
+
const project = detectProject(projectRoot);
|
|
67
|
+
if (project) return project.id;
|
|
68
|
+
} catch { /* fallback below */ }
|
|
69
|
+
const normalized = projectRoot.replace(/\\/g, '/');
|
|
70
|
+
return `untracked/${normalized}`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Record a file written by Memorix.
|
|
75
|
+
*/
|
|
76
|
+
export async function recordFile(
|
|
77
|
+
projectRoot: string,
|
|
78
|
+
type: AuditEntry['type'],
|
|
79
|
+
filePath: string,
|
|
80
|
+
agent?: string,
|
|
81
|
+
): Promise<void> {
|
|
82
|
+
const data = await loadAudit();
|
|
83
|
+
const projectId = getProjectId(projectRoot);
|
|
84
|
+
|
|
85
|
+
if (!data.projects[projectId]) {
|
|
86
|
+
data.projects[projectId] = {
|
|
87
|
+
projectRoot,
|
|
88
|
+
installedAt: new Date().toISOString(),
|
|
89
|
+
entries: [],
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Check if entry already exists
|
|
94
|
+
const existingIndex = data.projects[projectId].entries.findIndex(
|
|
95
|
+
(e) => e.path === filePath
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
if (existingIndex === -1) {
|
|
99
|
+
data.projects[projectId].entries.push({
|
|
100
|
+
type,
|
|
101
|
+
agent,
|
|
102
|
+
path: filePath,
|
|
103
|
+
createdAt: new Date().toISOString(),
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
await saveAudit(data);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Get all files written by Memorix for a project.
|
|
112
|
+
*/
|
|
113
|
+
export async function getProjectFiles(projectRoot: string): Promise<AuditEntry[]> {
|
|
114
|
+
const data = await loadAudit();
|
|
115
|
+
const projectId = getProjectId(projectRoot);
|
|
116
|
+
return data.projects[projectId]?.entries || [];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Remove a file from audit (when uninstalled).
|
|
121
|
+
*/
|
|
122
|
+
export async function removeFile(projectRoot: string, filePath: string): Promise<void> {
|
|
123
|
+
const data = await loadAudit();
|
|
124
|
+
const projectId = getProjectId(projectRoot);
|
|
125
|
+
|
|
126
|
+
if (!data.projects[projectId]) return;
|
|
127
|
+
|
|
128
|
+
data.projects[projectId].entries = data.projects[projectId].entries.filter(
|
|
129
|
+
(e) => e.path !== filePath
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
// If no entries left, remove the project
|
|
133
|
+
if (data.projects[projectId].entries.length === 0) {
|
|
134
|
+
delete data.projects[projectId];
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
await saveAudit(data);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Get all audit entries across all projects.
|
|
142
|
+
*/
|
|
143
|
+
export async function getAllAuditEntries(): Promise<
|
|
144
|
+
Array<{ projectId: string; entry: AuditEntry }>
|
|
145
|
+
> {
|
|
146
|
+
const data = await loadAudit();
|
|
147
|
+
const entries: Array<{ projectId: string; entry: AuditEntry }> = [];
|
|
148
|
+
|
|
149
|
+
for (const [projectId, project] of Object.entries(data.projects)) {
|
|
150
|
+
for (const entry of project.entries) {
|
|
151
|
+
entries.push({ projectId, entry });
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return entries;
|
|
156
|
+
}
|
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
|
|
20
20
|
import { defineCommand } from 'citty';
|
|
21
21
|
import * as fs from 'node:fs';
|
|
22
|
+
import path from 'node:path';
|
|
23
|
+
import { fileURLToPath } from 'node:url';
|
|
22
24
|
import { spawn, execFileSync } from 'node:child_process';
|
|
23
25
|
import { randomBytes } from 'node:crypto';
|
|
24
26
|
import { parseTcpPort } from '../port.js';
|
|
@@ -73,6 +75,33 @@ function parseBackgroundPort(value: string | undefined): number {
|
|
|
73
75
|
return parseTcpPort(value, 3211);
|
|
74
76
|
}
|
|
75
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Resolve the CLI file that `background start` should spawn.
|
|
80
|
+
* `process.argv[1]` follows the launcher shim and can point at a different
|
|
81
|
+
* install (for example `$HOME/node_modules/memorix` after `bun add` in $HOME).
|
|
82
|
+
* When this module is bundled into `dist/cli/index.js`, `../index.js` is the
|
|
83
|
+
* library stdio entry (`dist/index.js`) — never spawn that.
|
|
84
|
+
*/
|
|
85
|
+
function resolveBackgroundCliEntryFrom(here: string, argv1: string): string {
|
|
86
|
+
const dir = path.dirname(here);
|
|
87
|
+
if (path.basename(here) === 'index.js' && path.basename(dir) === 'cli') {
|
|
88
|
+
return here;
|
|
89
|
+
}
|
|
90
|
+
const compiledSibling = path.resolve(dir, '..', 'index.js');
|
|
91
|
+
if (fs.existsSync(compiledSibling) && path.basename(path.dirname(compiledSibling)) === 'cli') {
|
|
92
|
+
return compiledSibling;
|
|
93
|
+
}
|
|
94
|
+
return argv1;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function resolveBackgroundCliEntry(argv1 = process.argv[1]): string {
|
|
98
|
+
try {
|
|
99
|
+
return resolveBackgroundCliEntryFrom(fileURLToPath(import.meta.url), argv1);
|
|
100
|
+
} catch {
|
|
101
|
+
return argv1;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
76
105
|
function parseLogLines(value: string | undefined): number {
|
|
77
106
|
return parseBoundedPositiveInteger(value, 50, 'Log line count', 10_000);
|
|
78
107
|
}
|
|
@@ -343,8 +372,9 @@ export async function doStart(port: number): Promise<void> {
|
|
|
343
372
|
|
|
344
373
|
// 5. Find the memorix CLI entry point
|
|
345
374
|
// We need to spawn `node <cli-entry> serve-http --port <port>`
|
|
346
|
-
//
|
|
347
|
-
|
|
375
|
+
// Prefer this package's compiled CLI so a bun/npm shim cannot spawn a
|
|
376
|
+
// different (often unpatched) memorix from ~/node_modules.
|
|
377
|
+
const cliEntry = resolveBackgroundCliEntry();
|
|
348
378
|
|
|
349
379
|
// 6. Start the service without a visible console. A regular non-detached child
|
|
350
380
|
// can be reclaimed with its parent by terminal hosts on Windows; a detached Node
|
|
@@ -792,4 +822,6 @@ export const _testing = {
|
|
|
792
822
|
parseBackgroundPort,
|
|
793
823
|
parseLogLines,
|
|
794
824
|
formatBackgroundCommandError,
|
|
825
|
+
resolveBackgroundCliEntry,
|
|
826
|
+
resolveBackgroundCliEntryFrom,
|
|
795
827
|
};
|
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
import { defineCommand } from 'citty';
|
|
2
|
-
import { getBeforeAfterMetrics, getMetricsSummary } from '../../memory/formation/index.js';
|
|
3
|
-
import { emitError, emitResult } from './operator-shared.js';
|
|
4
|
-
|
|
5
|
-
export default defineCommand({
|
|
6
|
-
meta: {
|
|
7
|
-
name: 'formation',
|
|
8
|
-
description: 'Inspect Memory Formation Pipeline runtime metrics',
|
|
9
|
-
},
|
|
10
|
-
args: {
|
|
11
|
-
json: { type: 'boolean', description: 'Emit machine-readable JSON output' },
|
|
12
|
-
},
|
|
13
|
-
run: async ({ args }) => {
|
|
14
|
-
const action = (args._ as string[])?.[0] || '';
|
|
15
|
-
const asJson = !!args.json;
|
|
16
|
-
|
|
17
|
-
try {
|
|
18
|
-
switch (action) {
|
|
19
|
-
case 'metrics': {
|
|
20
|
-
const summary = getMetricsSummary();
|
|
21
|
-
const beforeAfter = getBeforeAfterMetrics();
|
|
22
|
-
emitResult(
|
|
23
|
-
{ summary, beforeAfter },
|
|
24
|
-
summary.total === 0
|
|
25
|
-
? 'Formation Pipeline: No metrics collected yet.'
|
|
26
|
-
: [
|
|
27
|
-
'Formation Pipeline Metrics',
|
|
28
|
-
`- Total observations processed: ${summary.total}`,
|
|
29
|
-
`- Average value score: ${summary.avgValueScore.toFixed(3)}`,
|
|
30
|
-
`- Average processing time: ${summary.avgDurationMs.toFixed(1)}ms`,
|
|
31
|
-
].join('\n'),
|
|
32
|
-
asJson,
|
|
33
|
-
);
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
default:
|
|
38
|
-
console.log('Memorix Formation Commands');
|
|
39
|
-
console.log('');
|
|
40
|
-
console.log('Usage:');
|
|
41
|
-
console.log(' memorix formation metrics');
|
|
42
|
-
}
|
|
43
|
-
} catch (error) {
|
|
44
|
-
emitError(error instanceof Error ? error.message : String(error), asJson);
|
|
45
|
-
}
|
|
46
|
-
},
|
|
47
|
-
});
|
|
48
|
-
|
|
1
|
+
import { defineCommand } from 'citty';
|
|
2
|
+
import { getBeforeAfterMetrics, getMetricsSummary } from '../../memory/formation/index.js';
|
|
3
|
+
import { emitError, emitResult } from './operator-shared.js';
|
|
4
|
+
|
|
5
|
+
export default defineCommand({
|
|
6
|
+
meta: {
|
|
7
|
+
name: 'formation',
|
|
8
|
+
description: 'Inspect Memory Formation Pipeline runtime metrics',
|
|
9
|
+
},
|
|
10
|
+
args: {
|
|
11
|
+
json: { type: 'boolean', description: 'Emit machine-readable JSON output' },
|
|
12
|
+
},
|
|
13
|
+
run: async ({ args }) => {
|
|
14
|
+
const action = (args._ as string[])?.[0] || '';
|
|
15
|
+
const asJson = !!args.json;
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
switch (action) {
|
|
19
|
+
case 'metrics': {
|
|
20
|
+
const summary = getMetricsSummary();
|
|
21
|
+
const beforeAfter = getBeforeAfterMetrics();
|
|
22
|
+
emitResult(
|
|
23
|
+
{ summary, beforeAfter },
|
|
24
|
+
summary.total === 0
|
|
25
|
+
? 'Formation Pipeline: No metrics collected yet.'
|
|
26
|
+
: [
|
|
27
|
+
'Formation Pipeline Metrics',
|
|
28
|
+
`- Total observations processed: ${summary.total}`,
|
|
29
|
+
`- Average value score: ${summary.avgValueScore.toFixed(3)}`,
|
|
30
|
+
`- Average processing time: ${summary.avgDurationMs.toFixed(1)}ms`,
|
|
31
|
+
].join('\n'),
|
|
32
|
+
asJson,
|
|
33
|
+
);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
default:
|
|
38
|
+
console.log('Memorix Formation Commands');
|
|
39
|
+
console.log('');
|
|
40
|
+
console.log('Usage:');
|
|
41
|
+
console.log(' memorix formation metrics');
|
|
42
|
+
}
|
|
43
|
+
} catch (error) {
|
|
44
|
+
emitError(error instanceof Error ? error.message : String(error), asJson);
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
|