@pyxmate/memory 1.26.6 → 1.27.1
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/chunk-4DXEQC6X.mjs +16 -0
- package/dist/chunk-7IGU3HJO.mjs +70 -0
- package/dist/chunk-7TATCSSN.mjs +650 -0
- package/dist/chunk-QR5K7ETI.mjs +793 -0
- package/dist/{chunk-KZKGRND2.mjs → chunk-RWQTBXUC.mjs} +12 -0
- package/dist/chunk-S3CX323Z.mjs +34 -0
- package/dist/chunk-THLUBOGD.mjs +263 -0
- package/dist/chunk-UESIQ47D.mjs +165 -0
- package/dist/chunk-YBPODKB5.mjs +132 -0
- package/dist/cli/pyx-mem.mjs +68 -4208
- package/dist/dashboard.mjs +1 -1
- package/dist/doctor-AERNUHEZ.mjs +143 -0
- package/dist/index.d.ts +8 -1
- package/dist/index.mjs +7 -1
- package/dist/insights-PD7Y6QDX.mjs +139 -0
- package/dist/login-4ALCAN4M.mjs +179 -0
- package/dist/login-browser-DO2O2Q2G.mjs +407 -0
- package/dist/login-recovery-AOO76PYX.mjs +97 -0
- package/dist/logout-4CO53LNA.mjs +13 -0
- package/dist/mcp-64KVRH7F.mjs +944 -0
- package/dist/mcp-install-KBB45OB2.mjs +36 -0
- package/dist/overview-COBXHLP3.mjs +88 -0
- package/dist/react.mjs +1 -1
- package/dist/scaffold-2FSCWF22.mjs +186 -0
- package/dist/status-UHJZLEPL.mjs +74 -0
- package/dist/taxonomy-EKDFDFXW.mjs +103 -0
- package/package.json +1 -1
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import {
|
|
2
|
+
installMemoryRules,
|
|
3
|
+
installMemoryRulesOnly,
|
|
4
|
+
mcpInstallClaudeCodeCommand,
|
|
5
|
+
mcpInstallClineCommand,
|
|
6
|
+
mcpInstallCodexCommand,
|
|
7
|
+
mcpInstallContinueCommand,
|
|
8
|
+
mcpInstallCursorCommand,
|
|
9
|
+
mcpInstallGeminiCliCommand,
|
|
10
|
+
mcpInstallHermesCommand,
|
|
11
|
+
mcpInstallOhMyPiCommand,
|
|
12
|
+
mcpInstallOpenClawCommand,
|
|
13
|
+
mcpInstallPiCommand,
|
|
14
|
+
mcpInstallWindsurfCommand,
|
|
15
|
+
reconcileMemoryRules,
|
|
16
|
+
syncMemoryRulesFromServer
|
|
17
|
+
} from "./chunk-QR5K7ETI.mjs";
|
|
18
|
+
import "./chunk-QD7G23CN.mjs";
|
|
19
|
+
import "./chunk-4DXEQC6X.mjs";
|
|
20
|
+
export {
|
|
21
|
+
installMemoryRules,
|
|
22
|
+
installMemoryRulesOnly,
|
|
23
|
+
mcpInstallClaudeCodeCommand,
|
|
24
|
+
mcpInstallClineCommand,
|
|
25
|
+
mcpInstallCodexCommand,
|
|
26
|
+
mcpInstallContinueCommand,
|
|
27
|
+
mcpInstallCursorCommand,
|
|
28
|
+
mcpInstallGeminiCliCommand,
|
|
29
|
+
mcpInstallHermesCommand,
|
|
30
|
+
mcpInstallOhMyPiCommand,
|
|
31
|
+
mcpInstallOpenClawCommand,
|
|
32
|
+
mcpInstallPiCommand,
|
|
33
|
+
mcpInstallWindsurfCommand,
|
|
34
|
+
reconcileMemoryRules,
|
|
35
|
+
syncMemoryRulesFromServer
|
|
36
|
+
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import {
|
|
2
|
+
readSurface
|
|
3
|
+
} from "./chunk-UESIQ47D.mjs";
|
|
4
|
+
import "./chunk-THLUBOGD.mjs";
|
|
5
|
+
import "./chunk-7TATCSSN.mjs";
|
|
6
|
+
import {
|
|
7
|
+
EXIT
|
|
8
|
+
} from "./chunk-4DXEQC6X.mjs";
|
|
9
|
+
|
|
10
|
+
// src/cli/commands/overview.ts
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
var OVERVIEW_SCHEMA_VERSION = 1;
|
|
13
|
+
var RECENT_ENTRY_LIMIT = 3;
|
|
14
|
+
var MAX_TOPIC_CHARS = 160;
|
|
15
|
+
var MAX_PROJECT_CHARS = 80;
|
|
16
|
+
var entrySchema = z.object({
|
|
17
|
+
id: z.string().min(1),
|
|
18
|
+
metadata: z.record(z.string(), z.unknown())
|
|
19
|
+
}).passthrough();
|
|
20
|
+
var hostedEntriesResponseSchema = z.object({
|
|
21
|
+
memories: z.array(entrySchema),
|
|
22
|
+
total: z.number().int().nonnegative()
|
|
23
|
+
}).passthrough();
|
|
24
|
+
var selfHostedEntriesResponseSchema = z.object({
|
|
25
|
+
success: z.literal(true),
|
|
26
|
+
data: z.object({
|
|
27
|
+
entries: z.array(entrySchema),
|
|
28
|
+
totalCount: z.number().int().nonnegative()
|
|
29
|
+
}).passthrough()
|
|
30
|
+
}).passthrough();
|
|
31
|
+
var entriesResponseSchema = z.union([
|
|
32
|
+
hostedEntriesResponseSchema.transform(({ memories, total }) => ({
|
|
33
|
+
entries: memories,
|
|
34
|
+
totalCount: total
|
|
35
|
+
})),
|
|
36
|
+
selfHostedEntriesResponseSchema.transform(({ data }) => data)
|
|
37
|
+
]);
|
|
38
|
+
async function overviewCommand(opts = {}) {
|
|
39
|
+
const result = await readSurface({
|
|
40
|
+
surface: "overview",
|
|
41
|
+
request: {
|
|
42
|
+
method: "GET",
|
|
43
|
+
path: "/api/memory/entries",
|
|
44
|
+
query: { status: "active", limit: RECENT_ENTRY_LIMIT }
|
|
45
|
+
},
|
|
46
|
+
schema: entriesResponseSchema,
|
|
47
|
+
keychain: opts.keychain,
|
|
48
|
+
fetchImpl: opts.fetchImpl
|
|
49
|
+
});
|
|
50
|
+
if (!result.ok) {
|
|
51
|
+
emit(report(result.state, result.endpoint, result.keySource, result.problem));
|
|
52
|
+
return result.exit;
|
|
53
|
+
}
|
|
54
|
+
emit({
|
|
55
|
+
...report("ready", result.endpoint, "keychain", null),
|
|
56
|
+
totalCount: result.data.totalCount,
|
|
57
|
+
entries: result.data.entries.slice(0, RECENT_ENTRY_LIMIT).map((entry) => ({
|
|
58
|
+
id: entry.id,
|
|
59
|
+
topic: boundedLabel(entry.metadata.topic, "Untitled", MAX_TOPIC_CHARS),
|
|
60
|
+
project: boundedLabel(entry.metadata.project, "Unscoped", MAX_PROJECT_CHARS)
|
|
61
|
+
}))
|
|
62
|
+
});
|
|
63
|
+
return EXIT.OK;
|
|
64
|
+
}
|
|
65
|
+
function report(state, endpoint, keySource, problem) {
|
|
66
|
+
return {
|
|
67
|
+
schemaVersion: OVERVIEW_SCHEMA_VERSION,
|
|
68
|
+
state,
|
|
69
|
+
endpoint,
|
|
70
|
+
keySource,
|
|
71
|
+
totalCount: 0,
|
|
72
|
+
entries: [],
|
|
73
|
+
problem
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function emit(value) {
|
|
77
|
+
process.stdout.write(`${JSON.stringify(value)}
|
|
78
|
+
`);
|
|
79
|
+
}
|
|
80
|
+
function boundedLabel(value, fallback, maxChars) {
|
|
81
|
+
if (typeof value !== "string" || value.trim().length === 0) return fallback;
|
|
82
|
+
const normalized = value.trim();
|
|
83
|
+
const chars = [...normalized];
|
|
84
|
+
return chars.length > maxChars ? `${chars.slice(0, maxChars).join("")}\u2026` : normalized;
|
|
85
|
+
}
|
|
86
|
+
export {
|
|
87
|
+
overviewCommand
|
|
88
|
+
};
|
package/dist/react.mjs
CHANGED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildDesignGuide
|
|
3
|
+
} from "./chunk-QD7G23CN.mjs";
|
|
4
|
+
import {
|
|
5
|
+
EXIT
|
|
6
|
+
} from "./chunk-4DXEQC6X.mjs";
|
|
7
|
+
|
|
8
|
+
// src/cli/commands/scaffold.ts
|
|
9
|
+
import { existsSync, mkdirSync, statSync, writeFileSync } from "fs";
|
|
10
|
+
import { basename, join, resolve } from "path";
|
|
11
|
+
var SERVER_IMAGE = "ghcr.io/pyx-corp/pyx-memory-v1:latest";
|
|
12
|
+
var DOCKER_COMPOSE = `services:
|
|
13
|
+
pyx-memory:
|
|
14
|
+
image: ${SERVER_IMAGE}
|
|
15
|
+
ports:
|
|
16
|
+
- "7822:7822"
|
|
17
|
+
volumes:
|
|
18
|
+
- pyx-memory-data:/data
|
|
19
|
+
environment:
|
|
20
|
+
DATA_DIR: "\${DATA_DIR:-/data}"
|
|
21
|
+
API_KEY: "\${API_KEY:-}"
|
|
22
|
+
ADMIN_API_KEY: "\${ADMIN_API_KEY:-}"
|
|
23
|
+
TENANT_MODE: "\${TENANT_MODE:-single}"
|
|
24
|
+
NEO4J_URL: "\${NEO4J_URL:-bolt://neo4j:7687}"
|
|
25
|
+
NEO4J_USERNAME: "\${NEO4J_USERNAME:-neo4j}"
|
|
26
|
+
NEO4J_PASSWORD: "\${NEO4J_PASSWORD:?Set NEO4J_PASSWORD in .env or the shell.}"
|
|
27
|
+
EMBEDDING_PROVIDER: "\${EMBEDDING_PROVIDER:-local}"
|
|
28
|
+
EMBEDDING_ENDPOINT: "\${EMBEDDING_ENDPOINT:-}"
|
|
29
|
+
EMBEDDING_DIMENSIONS: "\${EMBEDDING_DIMENSIONS:-}"
|
|
30
|
+
# Graph is built by the CALLING AGENT by default (it passes entities +
|
|
31
|
+
# relationships / triples) \u2014 the server runs no LLM. To opt into
|
|
32
|
+
# server-side BYO extraction, set EXTRACTION_PROVIDER=http and supply
|
|
33
|
+
# EXTRACTION_ENDPOINT in .env.
|
|
34
|
+
EXTRACTION_PROVIDER: "\${EXTRACTION_PROVIDER:-none}"
|
|
35
|
+
EXTRACTION_ENDPOINT: "\${EXTRACTION_ENDPOINT:-}"
|
|
36
|
+
EXTRACTION_API_KEY: "\${EXTRACTION_API_KEY:-}"
|
|
37
|
+
EXTRACTION_MODEL: "\${EXTRACTION_MODEL:-}"
|
|
38
|
+
ENCRYPTION_KEY: "\${ENCRYPTION_KEY:-}"
|
|
39
|
+
depends_on:
|
|
40
|
+
neo4j:
|
|
41
|
+
condition: service_healthy
|
|
42
|
+
|
|
43
|
+
neo4j:
|
|
44
|
+
image: neo4j:5-community
|
|
45
|
+
ports:
|
|
46
|
+
- "7474:7474"
|
|
47
|
+
- "7687:7687"
|
|
48
|
+
volumes:
|
|
49
|
+
- pyx-memory-neo4j-data:/data
|
|
50
|
+
- pyx-memory-neo4j-logs:/logs
|
|
51
|
+
environment:
|
|
52
|
+
NEO4J_AUTH: "neo4j/\${NEO4J_PASSWORD:?Set NEO4J_PASSWORD in .env or the shell.}"
|
|
53
|
+
NEO4J_PLUGINS: '["apoc"]'
|
|
54
|
+
healthcheck:
|
|
55
|
+
test: ["CMD", "neo4j", "status"]
|
|
56
|
+
interval: 10s
|
|
57
|
+
timeout: 10s
|
|
58
|
+
retries: 10
|
|
59
|
+
|
|
60
|
+
volumes:
|
|
61
|
+
pyx-memory-data:
|
|
62
|
+
pyx-memory-neo4j-data:
|
|
63
|
+
pyx-memory-neo4j-logs:
|
|
64
|
+
`;
|
|
65
|
+
var ENV_EXAMPLE = `# pyx-memory operator config
|
|
66
|
+
# Copy this file to .env, then fill the required values before running Docker Compose.
|
|
67
|
+
|
|
68
|
+
# OPTIONAL. Graph is built by the calling agent by default (the server runs no LLM).
|
|
69
|
+
# Set to http ONLY to opt into server-side BYO entity/relationship extraction.
|
|
70
|
+
EXTRACTION_PROVIDER=none
|
|
71
|
+
|
|
72
|
+
# REQUIRED only when EXTRACTION_PROVIDER=http. HTTP chat/completions-compatible base URL.
|
|
73
|
+
# Leave empty for the default agent-owns-the-graph setup.
|
|
74
|
+
EXTRACTION_ENDPOINT=
|
|
75
|
+
|
|
76
|
+
# OPTIONAL. Bearer token sent to EXTRACTION_ENDPOINT when your extraction service requires auth.
|
|
77
|
+
EXTRACTION_API_KEY=
|
|
78
|
+
|
|
79
|
+
# OPTIONAL. Model identifier sent to the extraction service.
|
|
80
|
+
EXTRACTION_MODEL=
|
|
81
|
+
|
|
82
|
+
# OPTIONAL for local embeddings. Set to http only when using a remote embedding service.
|
|
83
|
+
EMBEDDING_PROVIDER=local
|
|
84
|
+
|
|
85
|
+
# REQUIRED only when EMBEDDING_PROVIDER=http. Base URL for the HTTP embedding service.
|
|
86
|
+
EMBEDDING_ENDPOINT=
|
|
87
|
+
|
|
88
|
+
# REQUIRED only when EMBEDDING_PROVIDER=http. Must match the remote embedding vector width.
|
|
89
|
+
# Example for embeddinggemma: 768.
|
|
90
|
+
EMBEDDING_DIMENSIONS=
|
|
91
|
+
|
|
92
|
+
# RECOMMENDED for any protected or production deployment. Empty means unauthenticated local access.
|
|
93
|
+
API_KEY=
|
|
94
|
+
|
|
95
|
+
# OPTIONAL. Destructive/admin operations use this; when empty, the server falls back to API_KEY.
|
|
96
|
+
ADMIN_API_KEY=
|
|
97
|
+
|
|
98
|
+
# REQUIRED only when SENSITIVITY_POLICY=encrypt is configured on the server.
|
|
99
|
+
# 32 bytes as 64 hex chars or 44 base64 chars.
|
|
100
|
+
ENCRYPTION_KEY=
|
|
101
|
+
|
|
102
|
+
# REQUIRED by the scaffolded container.
|
|
103
|
+
DATA_DIR=/data
|
|
104
|
+
|
|
105
|
+
# REQUIRED. Use single for one app/tenant; use multi only behind authenticated tenant routing.
|
|
106
|
+
TENANT_MODE=single
|
|
107
|
+
|
|
108
|
+
# REQUIRED for graph storage in this compose stack.
|
|
109
|
+
NEO4J_URL=bolt://neo4j:7687
|
|
110
|
+
|
|
111
|
+
# OPTIONAL. Defaults to neo4j when unset.
|
|
112
|
+
NEO4J_USERNAME=neo4j
|
|
113
|
+
|
|
114
|
+
# REQUIRED by docker-compose.yml for the Neo4j service and pyx-memory graph connection.
|
|
115
|
+
NEO4J_PASSWORD=
|
|
116
|
+
`;
|
|
117
|
+
var MEMORY_TS = `import { createPyxMemory } from '@pyxmate/memory';
|
|
118
|
+
|
|
119
|
+
// Reads PYX_MEMORY_URL and PYX_MEMORY_API_KEY by default.
|
|
120
|
+
export const memory = createPyxMemory();
|
|
121
|
+
`;
|
|
122
|
+
function resolveTarget(args) {
|
|
123
|
+
const cwd = args.cwd ?? process.cwd();
|
|
124
|
+
if (args.name === true) {
|
|
125
|
+
process.stderr.write("Error: --name requires a directory name.\n");
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
const trimmedName = typeof args.name === "string" ? args.name.trim() : "";
|
|
129
|
+
if (typeof args.name === "string" && trimmedName.length === 0) {
|
|
130
|
+
process.stderr.write("Error: --name requires a non-empty directory name.\n");
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
const targetDir = trimmedName ? resolve(cwd, trimmedName) : cwd;
|
|
134
|
+
const appName = trimmedName || basename(resolve(cwd));
|
|
135
|
+
return { targetDir, appName };
|
|
136
|
+
}
|
|
137
|
+
function buildFiles(targetDir, appName) {
|
|
138
|
+
return [
|
|
139
|
+
{ path: join(targetDir, "docker-compose.yml"), contents: DOCKER_COMPOSE },
|
|
140
|
+
{ path: join(targetDir, ".env.example"), contents: ENV_EXAMPLE },
|
|
141
|
+
{ path: join(targetDir, "memory.ts"), contents: MEMORY_TS },
|
|
142
|
+
{
|
|
143
|
+
path: join(targetDir, "PYX_MEMORY_DESIGN_GUIDE.md"),
|
|
144
|
+
contents: buildDesignGuide({ appName })
|
|
145
|
+
}
|
|
146
|
+
];
|
|
147
|
+
}
|
|
148
|
+
function scaffoldCommand(args = {}) {
|
|
149
|
+
const target = resolveTarget(args);
|
|
150
|
+
if (target === null) return EXIT.USAGE;
|
|
151
|
+
if (existsSync(target.targetDir) && !statSync(target.targetDir).isDirectory()) {
|
|
152
|
+
process.stderr.write(`Error: target exists and is not a directory: ${target.targetDir}
|
|
153
|
+
`);
|
|
154
|
+
return EXIT.USAGE;
|
|
155
|
+
}
|
|
156
|
+
mkdirSync(target.targetDir, { recursive: true });
|
|
157
|
+
const created = [];
|
|
158
|
+
const skipped = [];
|
|
159
|
+
for (const file of buildFiles(target.targetDir, target.appName)) {
|
|
160
|
+
const relativePath = basename(file.path);
|
|
161
|
+
if (existsSync(file.path)) {
|
|
162
|
+
skipped.push(relativePath);
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
writeFileSync(file.path, file.contents, "utf8");
|
|
166
|
+
created.push(relativePath);
|
|
167
|
+
}
|
|
168
|
+
process.stdout.write(
|
|
169
|
+
[
|
|
170
|
+
`pyx-memory scaffold: ${target.targetDir}`,
|
|
171
|
+
`created: ${created.length > 0 ? created.join(", ") : "(none)"}`,
|
|
172
|
+
`skipped: ${skipped.length > 0 ? skipped.join(", ") : "(none)"}`,
|
|
173
|
+
"",
|
|
174
|
+
"Next steps:",
|
|
175
|
+
"1. Copy .env.example to .env and set NEO4J_PASSWORD (graph is built by the calling agent by default \u2014 set EXTRACTION_* only to opt into server-side BYO extraction).",
|
|
176
|
+
"2. Set API_KEY before using this beyond local development.",
|
|
177
|
+
"3. Run: docker compose up -d",
|
|
178
|
+
"4. Set PYX_MEMORY_URL=http://localhost:7822 and PYX_MEMORY_API_KEY to match API_KEY.",
|
|
179
|
+
""
|
|
180
|
+
].join("\n")
|
|
181
|
+
);
|
|
182
|
+
return EXIT.OK;
|
|
183
|
+
}
|
|
184
|
+
export {
|
|
185
|
+
scaffoldCommand
|
|
186
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import {
|
|
2
|
+
JournalInvalidError,
|
|
3
|
+
KeychainError,
|
|
4
|
+
convergenceSeconds,
|
|
5
|
+
credentialFingerprint,
|
|
6
|
+
getDefaultKeychain,
|
|
7
|
+
guardKeychain,
|
|
8
|
+
isEmpty,
|
|
9
|
+
loadJournal
|
|
10
|
+
} from "./chunk-7TATCSSN.mjs";
|
|
11
|
+
import {
|
|
12
|
+
EXIT
|
|
13
|
+
} from "./chunk-4DXEQC6X.mjs";
|
|
14
|
+
|
|
15
|
+
// src/cli/commands/status.ts
|
|
16
|
+
async function readLifecycle(provider) {
|
|
17
|
+
let serverConvergenceSeconds = null;
|
|
18
|
+
try {
|
|
19
|
+
const journal = await loadJournal(provider);
|
|
20
|
+
serverConvergenceSeconds = convergenceSeconds(journal);
|
|
21
|
+
if (isEmpty(journal)) return { cleanupPending: false, serverConvergenceSeconds };
|
|
22
|
+
} catch (err) {
|
|
23
|
+
if (!(err instanceof JournalInvalidError)) throw err;
|
|
24
|
+
}
|
|
25
|
+
return { cleanupPending: !await provider.isLocked(), serverConvergenceSeconds };
|
|
26
|
+
}
|
|
27
|
+
async function statusCommand(opts = {}) {
|
|
28
|
+
const provider = opts.keychain ? guardKeychain(opts.keychain) : getDefaultKeychain();
|
|
29
|
+
let report;
|
|
30
|
+
try {
|
|
31
|
+
const creds = await provider.read();
|
|
32
|
+
report = {
|
|
33
|
+
endpoint: creds?.endpoint ?? null,
|
|
34
|
+
keyPresent: creds !== null,
|
|
35
|
+
keySource: creds !== null ? "keychain" : null,
|
|
36
|
+
loggedIn: creds !== null,
|
|
37
|
+
credentialFingerprint: creds === null ? null : credentialFingerprint(creds.endpoint, creds.apiKey),
|
|
38
|
+
lifecycleVersion: 1,
|
|
39
|
+
connection: creds?.provenance === "browser-login" ? creds.connection ?? null : null,
|
|
40
|
+
...await readLifecycle(provider)
|
|
41
|
+
};
|
|
42
|
+
} catch (err) {
|
|
43
|
+
if (err instanceof KeychainError) return emitError(err, opts.json);
|
|
44
|
+
throw err;
|
|
45
|
+
}
|
|
46
|
+
if (opts.json) {
|
|
47
|
+
process.stdout.write(`${JSON.stringify(report)}
|
|
48
|
+
`);
|
|
49
|
+
} else {
|
|
50
|
+
const lines = [
|
|
51
|
+
`endpoint: ${report.endpoint ?? "(not set)"}`,
|
|
52
|
+
`credentials: ${report.loggedIn ? "present (keychain)" : "not logged in \u2014 run: pyx-mem login"}`
|
|
53
|
+
];
|
|
54
|
+
process.stdout.write(`${lines.join("\n")}
|
|
55
|
+
`);
|
|
56
|
+
}
|
|
57
|
+
return report.loggedIn ? EXIT.OK : EXIT.NOT_LOGGED_IN;
|
|
58
|
+
}
|
|
59
|
+
function emitError(err, json) {
|
|
60
|
+
if (json) {
|
|
61
|
+
process.stdout.write(
|
|
62
|
+
`${JSON.stringify({ ok: false, code: err.code, message: err.message, guidance: err.guidance })}
|
|
63
|
+
`
|
|
64
|
+
);
|
|
65
|
+
} else {
|
|
66
|
+
process.stderr.write(`Error: ${err.message}
|
|
67
|
+
${err.guidance}
|
|
68
|
+
`);
|
|
69
|
+
}
|
|
70
|
+
return err.exit;
|
|
71
|
+
}
|
|
72
|
+
export {
|
|
73
|
+
statusCommand
|
|
74
|
+
};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import {
|
|
2
|
+
readSurface
|
|
3
|
+
} from "./chunk-UESIQ47D.mjs";
|
|
4
|
+
import "./chunk-THLUBOGD.mjs";
|
|
5
|
+
import "./chunk-7TATCSSN.mjs";
|
|
6
|
+
import {
|
|
7
|
+
MEMORY_PROJECT_LABEL_MAX_CHARS,
|
|
8
|
+
TAXONOMY_MAX_CATEGORIES,
|
|
9
|
+
TAXONOMY_MAX_PROJECTS,
|
|
10
|
+
TAXONOMY_MAX_SAMPLE_TOPICS,
|
|
11
|
+
TAXONOMY_MAX_TOP_ENTITIES
|
|
12
|
+
} from "./chunk-RWQTBXUC.mjs";
|
|
13
|
+
import "./chunk-HTQBKJTD.mjs";
|
|
14
|
+
import {
|
|
15
|
+
EXIT
|
|
16
|
+
} from "./chunk-4DXEQC6X.mjs";
|
|
17
|
+
|
|
18
|
+
// src/cli/commands/taxonomy.ts
|
|
19
|
+
import { z } from "zod";
|
|
20
|
+
var TAXONOMY_SCHEMA_VERSION = 1;
|
|
21
|
+
var TAXONOMY_MAX_RETURNED_CATEGORIES = TAXONOMY_MAX_CATEGORIES + 1;
|
|
22
|
+
var taxonomyStateSchema = z.object({
|
|
23
|
+
clusters: z.array(
|
|
24
|
+
z.object({
|
|
25
|
+
size: z.number().int().nonnegative(),
|
|
26
|
+
currentName: z.string().nullable(),
|
|
27
|
+
categoryNodeId: z.string().nullable(),
|
|
28
|
+
projects: z.array(
|
|
29
|
+
z.object({
|
|
30
|
+
name: z.string().min(1).max(MEMORY_PROJECT_LABEL_MAX_CHARS),
|
|
31
|
+
memories: z.number().int().positive()
|
|
32
|
+
})
|
|
33
|
+
).max(TAXONOMY_MAX_PROJECTS),
|
|
34
|
+
sampleTopics: z.array(z.string()).max(TAXONOMY_MAX_SAMPLE_TOPICS),
|
|
35
|
+
topEntities: z.array(
|
|
36
|
+
z.object({
|
|
37
|
+
name: z.string(),
|
|
38
|
+
type: z.string(),
|
|
39
|
+
degree: z.number().int().nonnegative()
|
|
40
|
+
})
|
|
41
|
+
).max(TAXONOMY_MAX_TOP_ENTITIES)
|
|
42
|
+
})
|
|
43
|
+
).max(TAXONOMY_MAX_RETURNED_CATEGORIES),
|
|
44
|
+
totals: z.object({
|
|
45
|
+
clusters: z.number().int().nonnegative(),
|
|
46
|
+
unnamed: z.number().int().nonnegative(),
|
|
47
|
+
nodesConsidered: z.number().int().nonnegative()
|
|
48
|
+
})
|
|
49
|
+
});
|
|
50
|
+
var taxonomyResponseSchema = z.union([
|
|
51
|
+
taxonomyStateSchema,
|
|
52
|
+
z.object({ success: z.literal(true), data: taxonomyStateSchema }).transform(({ data }) => data)
|
|
53
|
+
]);
|
|
54
|
+
async function taxonomyCommand(opts = {}) {
|
|
55
|
+
const result = await readSurface({
|
|
56
|
+
surface: "taxonomy",
|
|
57
|
+
request: { method: "GET", path: "/api/memory/graph/taxonomy" },
|
|
58
|
+
schema: taxonomyResponseSchema,
|
|
59
|
+
minimumServiceVersion: "1.18.1",
|
|
60
|
+
keychain: opts.keychain,
|
|
61
|
+
fetchImpl: opts.fetchImpl
|
|
62
|
+
});
|
|
63
|
+
const withIdentity = opts.withIdentity === true;
|
|
64
|
+
if (!result.ok) {
|
|
65
|
+
emit(
|
|
66
|
+
report(result.state, result.endpoint, result.keySource, result.problem, {
|
|
67
|
+
withIdentity,
|
|
68
|
+
credentialFingerprint: result.credentialFingerprint
|
|
69
|
+
})
|
|
70
|
+
);
|
|
71
|
+
return result.exit;
|
|
72
|
+
}
|
|
73
|
+
emit({
|
|
74
|
+
...report("ready", result.endpoint, "keychain", null, {
|
|
75
|
+
withIdentity,
|
|
76
|
+
credentialFingerprint: result.credentialFingerprint
|
|
77
|
+
}),
|
|
78
|
+
clusters: result.data.clusters,
|
|
79
|
+
totals: result.data.totals
|
|
80
|
+
});
|
|
81
|
+
return EXIT.OK;
|
|
82
|
+
}
|
|
83
|
+
function report(state, endpoint, keySource, problem, identity) {
|
|
84
|
+
return {
|
|
85
|
+
schemaVersion: TAXONOMY_SCHEMA_VERSION,
|
|
86
|
+
state,
|
|
87
|
+
endpoint,
|
|
88
|
+
keySource,
|
|
89
|
+
// Spread, not `undefined`: JSON.stringify would drop an undefined value,
|
|
90
|
+
// but the key must be absent from the object too so exact-shape checks hold.
|
|
91
|
+
...identity.withIdentity ? { credentialFingerprint: identity.credentialFingerprint } : {},
|
|
92
|
+
clusters: [],
|
|
93
|
+
totals: { clusters: 0, unnamed: 0, nodesConsidered: 0 },
|
|
94
|
+
problem
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function emit(value) {
|
|
98
|
+
process.stdout.write(`${JSON.stringify(value)}
|
|
99
|
+
`);
|
|
100
|
+
}
|
|
101
|
+
export {
|
|
102
|
+
taxonomyCommand
|
|
103
|
+
};
|