enigma-memory 0.1.5 → 0.1.7
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 +66 -35
- package/apps/cli/bin/enigma.mjs +768 -67
- package/docs/developer-ecosystem.md +92 -6
- package/docs/sdk-api.md +1 -1
- package/package.json +1 -1
- package/packages/mcp-server/src/index.js +1 -1
- package/scripts/build-installer-assets.mjs +1 -1
- package/scripts/run-standard-memory-benchmarks.mjs +1 -1
package/README.md
CHANGED
|
@@ -1,48 +1,62 @@
|
|
|
1
1
|
# Enigma
|
|
2
2
|
|
|
3
|
-
Enigma is a
|
|
3
|
+
Enigma Memory is a local-first AI Memory Passport. It gives you one canonical local memory vault, receipt-backed proof for Enigma-controlled events, and MCP connections into assistants such as Claude Desktop, Cursor, Kimi Code, and generic MCP clients.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Start with the package path below. You do not need to understand relay, gateway, mesh, browser, desktop, or hosted infrastructure before trying Enigma locally.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- Published npm package: package bins and module entry points are available as `enigma-memory`; use the npm install path below for the simplest onboarding flow.
|
|
9
|
-
- Source-only artifacts: `docs/`, `Dockerfile`, and `docker-compose.yml` live in the source checkout. The package README and CLI help are the package-included install guides; the full runbooks require the repository or hosted docs.
|
|
10
|
-
- Hosted cloud is not included by default. Hosted relay/gateway/cloud operation requires deployment credentials, a domain, TLS, production durable storage, KMS/secrets, monitoring, backups, operator policy, and a completed operator acceptance packet. Local relay/gateway `--state-file` demo state does not satisfy those hosted/BYOC requirements.
|
|
11
|
-
- Cloudflare API/domain/hosting automation is documented but safe-by-default: [`docs/cloudflare-token-and-domain-runbook.md`](docs/cloudflare-token-and-domain-runbook.md) gives the token recipe, Registrar prerequisites, local token storage rule, search/check flow, explicit domain+price purchase gate, Pages deploy gate, custom-domain steps, and post-setup token rotation.
|
|
12
|
-
- Overnight execution plan: [`docs/overnight-build-master-plan.md`](docs/overnight-build-master-plan.md) defines the GPT-5.5/Kimi overnight build cadence, acceptance gates, exact non-claims, and hosted/BYOC blockers.
|
|
13
|
-
- Security and production review artifacts: [`SECURITY.md`](SECURITY.md) defines reporting, safe harbor, disclosure, incident, secret-handling, plaintext-minimization, and proof-boundary policy; [`docs/security-threat-model.md`](docs/security-threat-model.md) maps assets, trust boundaries, controls, residual risks, and verification evidence; [`docs/operator-acceptance-packet.md`](docs/operator-acceptance-packet.md) is required before hosted/BYOC can be called live.
|
|
14
|
-
- Public API reference: [`docs/public-api-reference.md`](docs/public-api-reference.md) lists package exports, CLI bins, MCP tools/resources/prompts, relay/gateway endpoints, importer/capsule APIs, connector profiles, verifier outputs, schemas, and local-vs-hosted boundaries.
|
|
15
|
-
- Local release provenance/SBOM: [`docs/release-provenance-and-sbom.md`](docs/release-provenance-and-sbom.md) documents `npm run provenance:local -- --out ./.enigma/release-provenance.json` as unsigned local package-surface inventory and SHA-256 evidence only, not signed attestation, registry provenance, source-control proof, SLSA/compliance, Docker image, or hosted/cloud deployment evidence.
|
|
16
|
-
- Reviewer packet: [`docs/reviewer-packet.md`](docs/reviewer-packet.md) documents `npm run review:packet -- --out ./.enigma-review-packet --public-site <path-to-_public_site>` as a local hand-review bundle for package, release-audit, provenance, and optional generated public-site evidence; it is not npm publication, live Cloudflare deployment, Docker runtime proof, hosted/BYOC readiness, legal approval, signed provenance, or compliance evidence.
|
|
7
|
+
The package path needs no database, provider credential, cloud credential, npm publishing token, or external account. Hosted cloud and BYOC operation are separate advanced modes that require real deployment credentials, domains, TLS, durable storage, KMS/secrets, monitoring, backups, and operator acceptance.
|
|
17
8
|
|
|
18
9
|
Enigma does not claim that a closed provider deleted internal data, that model weights forgot, or that provider-native memory disappeared. It proves facts about Enigma-controlled vault state, receipts, checkpoints, and declared boundary operations.
|
|
19
10
|
|
|
20
|
-
## Install
|
|
11
|
+
## Install once, use everywhere
|
|
21
12
|
|
|
22
13
|
Prerequisites:
|
|
23
14
|
|
|
24
15
|
- Node.js `>=24`
|
|
25
|
-
- No database, package registry account, provider credential, or cloud credential for the local
|
|
16
|
+
- No database, package registry account, provider credential, or cloud credential for the local setup path
|
|
26
17
|
- Git only when you choose the advanced source-checkout path
|
|
27
18
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
Use the published package first:
|
|
19
|
+
Use Enigma as a one-time AI Memory Passport setup: install the package, create the local Enigma workspace once, then use the same memory/search/context/verify/connect loop from any supported client.
|
|
31
20
|
|
|
32
21
|
```sh
|
|
33
22
|
npm install -g enigma-memory
|
|
34
|
-
enigma
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
enigma-
|
|
23
|
+
enigma setup --overwrite
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`enigma setup --overwrite` is the safe default. It writes local Enigma artifacts under the workspace `.enigma` path and emits deterministic, public-safe JSON without printing raw memory plaintext. It does not write Claude, Cursor, Kimi, or other third-party app configs.
|
|
27
|
+
|
|
28
|
+
To let setup detect installed or already-configured clients and show the connector plan without mutating client configs:
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
enigma setup --client auto --overwrite
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`--client auto` selects clients found by connector detection and falls back to the default setup client list when none are present. The setup output lists which clients were selected, which were skipped, and why.
|
|
35
|
+
|
|
36
|
+
When you are ready to explicitly write connector entries for installed/config-present clients only:
|
|
37
|
+
|
|
38
|
+
```sh
|
|
39
|
+
enigma setup --connect-installed --overwrite
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`--connect-installed` implies auto client selection and is an explicit client-config write flag. It skips missing client configs instead of creating every default client config. Only explicit write flags mutate client configs. Existing explicit connector writes remain available through `enigma connect <client>` without `--dry-run`, and existing `enigma setup --write-connectors` behavior for explicit/default setup clients is unchanged; keep `--dry-run` while reviewing a single planned MCP entry.
|
|
43
|
+
|
|
44
|
+
After setup, use the same local vault from the CLI or connected clients:
|
|
45
|
+
|
|
46
|
+
```sh
|
|
47
|
+
enigma remember --text-file ./memory.txt
|
|
48
|
+
enigma search --query "..."
|
|
49
|
+
enigma context --query "..." --optimize
|
|
50
|
+
enigma verify --export ./.enigma/export.json
|
|
51
|
+
enigma connect claude-desktop --dry-run
|
|
38
52
|
```
|
|
39
53
|
|
|
40
|
-
|
|
54
|
+
The local Enigma vault remains canonical. Provider-native memory is non-canonical and should be treated as a convenience cache only. Enigma receipts prove Enigma-controlled vault state, receipts, checkpoints, and declared boundary operations; they do not prove provider deletion, provider model forgetting, provider-native memory removal, hosted availability, ROI/savings, or compliance certification.
|
|
41
55
|
|
|
42
56
|
One-off execution without a global install:
|
|
43
57
|
|
|
44
58
|
```sh
|
|
45
|
-
npx --yes --package enigma-memory enigma
|
|
59
|
+
npx --yes --package enigma-memory enigma setup --overwrite
|
|
46
60
|
```
|
|
47
61
|
|
|
48
62
|
## Advanced/source-only path
|
|
@@ -60,7 +74,7 @@ enigma-gateway demo
|
|
|
60
74
|
|
|
61
75
|
`install:local` is dry-run unless `--execute` is present. The command above installs the checked-out package globally and creates a local vault bundle. It does not require Cloudflare, OpenAI, Anthropic, npm publish credentials, a database, or hosted infrastructure.
|
|
62
76
|
|
|
63
|
-
Manual alternative:
|
|
77
|
+
Manual alternative: inspect the lower-level local vault steps that `enigma setup` prepares for you. Use a tenant-approved smoke file; do not expand private memory into shell argv.
|
|
64
78
|
|
|
65
79
|
POSIX shell:
|
|
66
80
|
|
|
@@ -72,7 +86,7 @@ enigma init --bundle ./.enigma/bundle.json --subject local-user --display-name "
|
|
|
72
86
|
enigma remember --bundle ./.enigma/bundle.json --text-file "$ENIGMA_DEMO_MEMORY_FILE" --purpose user_memory --tags local
|
|
73
87
|
enigma context --bundle ./.enigma/bundle.json --query "local context" --purpose local_answer --out ./.enigma/context-pack.json
|
|
74
88
|
enigma export --bundle ./.enigma/bundle.json --out ./.enigma/export.json
|
|
75
|
-
enigma verify --
|
|
89
|
+
enigma verify --export ./.enigma/export.json
|
|
76
90
|
```
|
|
77
91
|
|
|
78
92
|
Windows PowerShell:
|
|
@@ -85,7 +99,7 @@ enigma init --bundle .\.enigma\bundle.json --subject local-user --display-name "
|
|
|
85
99
|
enigma remember --bundle .\.enigma\bundle.json --text-file $env:ENIGMA_DEMO_MEMORY_FILE --purpose user_memory --tags local
|
|
86
100
|
enigma context --bundle .\.enigma\bundle.json --query "local context" --purpose local_answer --out .\.enigma\context-pack.json
|
|
87
101
|
enigma export --bundle .\.enigma\bundle.json --out .\.enigma\export.json
|
|
88
|
-
enigma verify --
|
|
102
|
+
enigma verify --export .\.enigma\export.json
|
|
89
103
|
```
|
|
90
104
|
|
|
91
105
|
The bundle is local. Exported proof artifacts contain encrypted/committed vault state and receipt metadata; do not paste raw memory plaintext into relay records, witness checkpoints, SIEM events, public proof artifacts, or shell command lines.
|
|
@@ -150,16 +164,33 @@ Supported connector profiles are:
|
|
|
150
164
|
- `opencode`
|
|
151
165
|
- `generic-mcp`
|
|
152
166
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
CLI connector commands:
|
|
167
|
+
After `enigma setup --overwrite`, preview a client connection before writing anything:
|
|
156
168
|
|
|
157
169
|
```sh
|
|
158
|
-
enigma
|
|
159
|
-
enigma
|
|
160
|
-
enigma connect
|
|
161
|
-
enigma connect
|
|
162
|
-
|
|
170
|
+
enigma connect claude-desktop --dry-run
|
|
171
|
+
enigma connect cursor --dry-run
|
|
172
|
+
enigma connect kimi-code --dry-run
|
|
173
|
+
enigma connect generic-mcp --dry-run
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
For the one-time setup flow, `enigma setup --client auto --overwrite` reports the installed/config-present clients that connector detection selected and the clients it skipped with reasons. It remains read-only for client configs. `enigma setup --connect-installed --overwrite` is the explicit setup-time write path for installed/config-present clients only; missing configs are skipped rather than created.
|
|
177
|
+
|
|
178
|
+
Remove `--dry-run` from `enigma connect <client>` only after you are ready for Enigma to merge the `mcpServers.enigma` entry into that specific client config. The safe default setup command never writes third-party app configs.
|
|
179
|
+
|
|
180
|
+
Manual MCP entry for Claude Desktop, Cursor, Kimi Code, or any generic MCP client:
|
|
181
|
+
|
|
182
|
+
```json
|
|
183
|
+
{
|
|
184
|
+
"mcpServers": {
|
|
185
|
+
"enigma": {
|
|
186
|
+
"command": "enigma-mcp",
|
|
187
|
+
"args": [],
|
|
188
|
+
"env": {
|
|
189
|
+
"ENIGMA_BUNDLE": "/absolute/path/to/.enigma/bundle.json"
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
163
194
|
```
|
|
164
195
|
|
|
165
196
|
Use `--mcp-command` (alias `--command`) when a GUI app cannot find shell-installed binaries or needs a `.cmd` path on Windows.
|
|
@@ -291,7 +322,7 @@ test -f "$ENIGMA_DEMO_MEMORY_FILE"
|
|
|
291
322
|
enigma init --bundle ./.enigma/bundle.json
|
|
292
323
|
enigma remember --bundle ./.enigma/bundle.json --text-file "$ENIGMA_DEMO_MEMORY_FILE" --purpose local_test
|
|
293
324
|
enigma export --bundle ./.enigma/bundle.json --out ./.enigma/export.json
|
|
294
|
-
enigma verify --
|
|
325
|
+
enigma verify --export ./.enigma/export.json
|
|
295
326
|
enigma boundary run --scenario committed_crossing
|
|
296
327
|
```
|
|
297
328
|
|
package/apps/cli/bin/enigma.mjs
CHANGED
|
@@ -11,13 +11,17 @@ import { runBoundarySimulation } from '../../../packages/boundary/src/index.js';
|
|
|
11
11
|
import { startStdioServer } from '../../../packages/mcp-server/src/index.js';
|
|
12
12
|
import { runMeshDemo } from '../../../packages/mesh/src/index.js';
|
|
13
13
|
import { runEnterpriseDemo } from '../../../packages/enterprise/src/index.js';
|
|
14
|
-
import { connectClient, disconnectClient, doctorConnectors, getClientProfile, renderMcpConfig, supportedClients } from '../../../packages/connectors/src/index.js';
|
|
14
|
+
import { connectClient, disconnectClient, doctorConnectors, getClientProfile, planConnectWizard, renderMcpConfig, supportedClients } from '../../../packages/connectors/src/index.js';
|
|
15
15
|
import { exportEnigmaCapsule, importChatGptExport, importClaudeMemory, importEnigmaCapsule, importLangGraphStore, importLettaAgentFile, importMem0Export, importZepGraphitiExport } from '../../../packages/importers/src/index.js';
|
|
16
16
|
import * as relayServer from '../../relay/src/server.mjs';
|
|
17
17
|
import * as gatewayServer from '../../gateway/src/server.mjs';
|
|
18
18
|
import { verifyBundle } from '../../verifier/bin/enigma-verify.mjs';
|
|
19
19
|
import { createNativeHostInstallPlan, createNativeHostManifest } from '../../native-host/bin/enigma-native-host.mjs';
|
|
20
20
|
import { aggregateUsageEvents, createUsageEvent } from '../../../packages/metering/src/index.js';
|
|
21
|
+
import {
|
|
22
|
+
createMemoryAccessReceipt,
|
|
23
|
+
createMemoryOptimizationPlan,
|
|
24
|
+
} from '../../../packages/optimizer/src/index.js';
|
|
21
25
|
import {
|
|
22
26
|
createConsumerGpuCapacityProfile,
|
|
23
27
|
createOperatorServiceQuote,
|
|
@@ -33,6 +37,21 @@ export const DEFAULT_GATEWAY_PORT = 8797;
|
|
|
33
37
|
const DEFAULT_QUICKSTART_MEMORY = 'Enigma quickstart demo memory: local proof bundles can be created and verified without provider or cloud credentials.';
|
|
34
38
|
const DEFAULT_CROSS_MODEL_DEMO_BUNDLE = '.enigma/cross-model-demo-bundle.json';
|
|
35
39
|
const DEFAULT_CROSS_MODEL_MEMORY = 'Enigma cross-model demo memory: a local encrypted memory can be packaged for ChatGPT, Claude, Kimi, Cursor, and a local LLM without provider credentials.';
|
|
40
|
+
const DEFAULT_SETUP_CLIENTS = Object.freeze(['generic-mcp', 'claude-desktop', 'cursor', 'kimi-code']);
|
|
41
|
+
const SETUP_CLAIM_BOUNDARIES = Object.freeze({
|
|
42
|
+
local_only: true,
|
|
43
|
+
provider_credentials_required: false,
|
|
44
|
+
provider_native_memory_canonical: false,
|
|
45
|
+
provider_deletion_proof: false,
|
|
46
|
+
model_forgetting_proof: false,
|
|
47
|
+
roi_or_savings_guarantee: false,
|
|
48
|
+
compliance_certification: false,
|
|
49
|
+
});
|
|
50
|
+
const QUICKSTART_ARTIFACT_NAMES = Object.freeze({
|
|
51
|
+
contextPack: 'context-pack.json',
|
|
52
|
+
export: 'export.json',
|
|
53
|
+
verifyReport: 'verify-report.json',
|
|
54
|
+
});
|
|
36
55
|
const CROSS_MODEL_PROFILES = Object.freeze([
|
|
37
56
|
{ id: 'chatgpt', provider: 'chatgpt', model: 'chatgpt-mcp-profile', label: 'ChatGPT' },
|
|
38
57
|
{ id: 'claude', provider: 'claude', model: 'claude-mcp-profile', label: 'Claude' },
|
|
@@ -77,6 +96,19 @@ const IMPORTERS = Object.freeze({
|
|
|
77
96
|
|
|
78
97
|
export const REQUIRED_PACKAGE_BINS = Object.freeze(['enigma', 'enigma-verify', 'enigma-mcp', 'enigma-relay', 'enigma-gateway', 'enigma-native-host']);
|
|
79
98
|
|
|
99
|
+
function setFlagValue(flags, name, value) {
|
|
100
|
+
if (!flags.has(name)) {
|
|
101
|
+
flags.set(name, value);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const current = flags.get(name);
|
|
105
|
+
if (Array.isArray(current)) {
|
|
106
|
+
current.push(value);
|
|
107
|
+
} else {
|
|
108
|
+
flags.set(name, [current, value]);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
80
112
|
function parseArgs(argv) {
|
|
81
113
|
const flags = new Map();
|
|
82
114
|
for (let i = 0; i < argv.length; i += 1) {
|
|
@@ -84,11 +116,11 @@ function parseArgs(argv) {
|
|
|
84
116
|
if (!arg.startsWith('--')) continue;
|
|
85
117
|
const eq = arg.indexOf('=');
|
|
86
118
|
if (eq !== -1) {
|
|
87
|
-
flags
|
|
119
|
+
setFlagValue(flags, arg.slice(2, eq), arg.slice(eq + 1));
|
|
88
120
|
} else if (!argv[i + 1] || argv[i + 1].startsWith('--')) {
|
|
89
|
-
flags
|
|
121
|
+
setFlagValue(flags, arg.slice(2), true);
|
|
90
122
|
} else {
|
|
91
|
-
flags
|
|
123
|
+
setFlagValue(flags, arg.slice(2), argv[i + 1]);
|
|
92
124
|
i += 1;
|
|
93
125
|
}
|
|
94
126
|
}
|
|
@@ -100,6 +132,19 @@ function getFlag(flags, names, fallback = undefined) {
|
|
|
100
132
|
return fallback;
|
|
101
133
|
}
|
|
102
134
|
|
|
135
|
+
function lastFlagValue(value) {
|
|
136
|
+
return Array.isArray(value) ? value[value.length - 1] : value;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function booleanFlag(flags, names, fallback = false) {
|
|
140
|
+
const value = lastFlagValue(getFlag(flags, names, fallback));
|
|
141
|
+
if (value === true || value === false) return value;
|
|
142
|
+
if (value === undefined || value === '') return fallback;
|
|
143
|
+
if (String(value) === 'true') return true;
|
|
144
|
+
if (String(value) === 'false') return false;
|
|
145
|
+
throw new Error(`--${names[0]} must be true or false.`);
|
|
146
|
+
}
|
|
147
|
+
|
|
103
148
|
function requireFlag(flags, names, label = names[0]) {
|
|
104
149
|
const value = getFlag(flags, names);
|
|
105
150
|
if (value === undefined || value === true || value === '') throw new Error(`Missing required --${label}.`);
|
|
@@ -166,7 +211,7 @@ async function fileExists(path) {
|
|
|
166
211
|
}
|
|
167
212
|
|
|
168
213
|
function pathFlag(flags, names, fallback) {
|
|
169
|
-
const value = getFlag(flags, names, fallback);
|
|
214
|
+
const value = lastFlagValue(getFlag(flags, names, fallback));
|
|
170
215
|
if (value === true || value === '') throw new Error(`Missing required --${names[0]}.`);
|
|
171
216
|
return String(value);
|
|
172
217
|
}
|
|
@@ -210,6 +255,74 @@ async function quickstartMemoryTextFromFlags(flags) {
|
|
|
210
255
|
return DEFAULT_QUICKSTART_MEMORY;
|
|
211
256
|
}
|
|
212
257
|
|
|
258
|
+
function quickstartOutputs(bundleInput, outDirInput) {
|
|
259
|
+
const bundlePath = resolve(bundleInput);
|
|
260
|
+
const outDirPath = resolve(outDirInput);
|
|
261
|
+
const contextPackPath = resolve(outDirPath, QUICKSTART_ARTIFACT_NAMES.contextPack);
|
|
262
|
+
const exportPath = resolve(outDirPath, QUICKSTART_ARTIFACT_NAMES.export);
|
|
263
|
+
const verifyReportPath = resolve(outDirPath, QUICKSTART_ARTIFACT_NAMES.verifyReport);
|
|
264
|
+
return {
|
|
265
|
+
bundlePath,
|
|
266
|
+
outDirPath,
|
|
267
|
+
contextPackPath,
|
|
268
|
+
exportPath,
|
|
269
|
+
verifyReportPath,
|
|
270
|
+
contextPackDisplay: quickstartPathDisplay(outDirInput, QUICKSTART_ARTIFACT_NAMES.contextPack),
|
|
271
|
+
exportDisplay: quickstartPathDisplay(outDirInput, QUICKSTART_ARTIFACT_NAMES.export),
|
|
272
|
+
verifyReportDisplay: quickstartPathDisplay(outDirInput, QUICKSTART_ARTIFACT_NAMES.verifyReport),
|
|
273
|
+
outputs: [
|
|
274
|
+
{ path: bundlePath, display: bundleInput },
|
|
275
|
+
{ path: contextPackPath, display: quickstartPathDisplay(outDirInput, QUICKSTART_ARTIFACT_NAMES.contextPack) },
|
|
276
|
+
{ path: exportPath, display: quickstartPathDisplay(outDirInput, QUICKSTART_ARTIFACT_NAMES.export) },
|
|
277
|
+
{ path: verifyReportPath, display: quickstartPathDisplay(outDirInput, QUICKSTART_ARTIFACT_NAMES.verifyReport) },
|
|
278
|
+
],
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
async function buildQuickstartArtifacts(flags, { bundleInput = DEFAULT_BUNDLE, outDirInput = dirname(bundleInput), overwrite = false, write = true } = {}) {
|
|
283
|
+
const paths = quickstartOutputs(bundleInput, outDirInput);
|
|
284
|
+
ensureDistinctOutputPaths(paths.outputs.map((output) => output.path));
|
|
285
|
+
await assertCanWriteQuickstartOutputs(paths.outputs, overwrite);
|
|
286
|
+
|
|
287
|
+
const vault = createVault({
|
|
288
|
+
subjectId: String(getFlag(flags, ['subject', 'subject-id'], 'local-user')),
|
|
289
|
+
displayName: String(getFlag(flags, ['display-name', 'name'], 'Local user')),
|
|
290
|
+
passphrase: String(getFlag(flags, ['passphrase'], 'local-development-passphrase')),
|
|
291
|
+
});
|
|
292
|
+
const passport = createPassport({
|
|
293
|
+
vault,
|
|
294
|
+
subjectId: vault.subject_id,
|
|
295
|
+
displayName: String(getFlag(flags, ['display-name', 'name'], 'Local user')),
|
|
296
|
+
});
|
|
297
|
+
remember({
|
|
298
|
+
vault,
|
|
299
|
+
passport,
|
|
300
|
+
text: await quickstartMemoryTextFromFlags(flags),
|
|
301
|
+
purpose: 'quickstart_local_proof',
|
|
302
|
+
purpose_tags: ['quickstart'],
|
|
303
|
+
metadata: { source: 'enigma quickstart' },
|
|
304
|
+
});
|
|
305
|
+
const contextPack = compileContextPack({
|
|
306
|
+
vault,
|
|
307
|
+
passport,
|
|
308
|
+
query: '',
|
|
309
|
+
purpose: 'quickstart_local_context',
|
|
310
|
+
limit: 8,
|
|
311
|
+
});
|
|
312
|
+
const exported = exportBundle({ vault, includePlaintext: false });
|
|
313
|
+
const bundle = exported.bundle ?? exported;
|
|
314
|
+
const verifyReport = verifyBundle(bundle);
|
|
315
|
+
|
|
316
|
+
if (write) {
|
|
317
|
+
await writeJson(paths.bundlePath, bundle);
|
|
318
|
+
await writeJson(paths.contextPackPath, contextPack);
|
|
319
|
+
await writeJson(paths.exportPath, bundle);
|
|
320
|
+
await writeJson(paths.verifyReportPath, verifyReport);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return { ...paths, vault, passport, contextPack, bundle, verifyReport };
|
|
324
|
+
}
|
|
325
|
+
|
|
213
326
|
async function crossModelMemoryTextFromFlags(flags) {
|
|
214
327
|
const textFile = getFlag(flags, ['memory-file', 'memoryFile', 'text-file', 'textFile']);
|
|
215
328
|
if (textFile === undefined) return DEFAULT_CROSS_MODEL_MEMORY;
|
|
@@ -273,6 +386,157 @@ function publicContextPackSummary(pack) {
|
|
|
273
386
|
};
|
|
274
387
|
}
|
|
275
388
|
|
|
389
|
+
const SEARCH_RELEVANCE_STOPWORDS = new Set([
|
|
390
|
+
'about',
|
|
391
|
+
'after',
|
|
392
|
+
'again',
|
|
393
|
+
'against',
|
|
394
|
+
'also',
|
|
395
|
+
'and',
|
|
396
|
+
'any',
|
|
397
|
+
'are',
|
|
398
|
+
'assistant',
|
|
399
|
+
'because',
|
|
400
|
+
'been',
|
|
401
|
+
'before',
|
|
402
|
+
'being',
|
|
403
|
+
'between',
|
|
404
|
+
'can',
|
|
405
|
+
'could',
|
|
406
|
+
'current',
|
|
407
|
+
'does',
|
|
408
|
+
'from',
|
|
409
|
+
'has',
|
|
410
|
+
'have',
|
|
411
|
+
'how',
|
|
412
|
+
'into',
|
|
413
|
+
'its',
|
|
414
|
+
'latest',
|
|
415
|
+
'more',
|
|
416
|
+
'most',
|
|
417
|
+
'number',
|
|
418
|
+
'own',
|
|
419
|
+
'owns',
|
|
420
|
+
'please',
|
|
421
|
+
'should',
|
|
422
|
+
'that',
|
|
423
|
+
'the',
|
|
424
|
+
'their',
|
|
425
|
+
'then',
|
|
426
|
+
'there',
|
|
427
|
+
'these',
|
|
428
|
+
'they',
|
|
429
|
+
'this',
|
|
430
|
+
'use',
|
|
431
|
+
'using',
|
|
432
|
+
'was',
|
|
433
|
+
'what',
|
|
434
|
+
'when',
|
|
435
|
+
'where',
|
|
436
|
+
'which',
|
|
437
|
+
'who',
|
|
438
|
+
'whose',
|
|
439
|
+
'why',
|
|
440
|
+
'with',
|
|
441
|
+
'would',
|
|
442
|
+
]);
|
|
443
|
+
|
|
444
|
+
function addSearchToken(tokens, token) {
|
|
445
|
+
if (token.length < 3) return;
|
|
446
|
+
if (!/[a-z]/u.test(token)) return;
|
|
447
|
+
if (SEARCH_RELEVANCE_STOPWORDS.has(token)) return;
|
|
448
|
+
tokens.add(token);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
function searchTokensFrom(value) {
|
|
452
|
+
const tokens = new Set();
|
|
453
|
+
if (value === undefined || value === null) return tokens;
|
|
454
|
+
for (const match of String(value).toLowerCase().matchAll(/[a-z0-9]+(?:[-_][a-z0-9]+)*/gu)) {
|
|
455
|
+
const token = match[0];
|
|
456
|
+
addSearchToken(tokens, token);
|
|
457
|
+
if (token.includes('-') || token.includes('_')) {
|
|
458
|
+
for (const part of token.split(/[-_]+/u)) addSearchToken(tokens, part);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
return tokens;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
function addSearchTokensFromValue(tokens, value) {
|
|
465
|
+
for (const token of searchTokensFrom(value)) tokens.add(token);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
function recordSearchTokens(record, content) {
|
|
469
|
+
const tokens = new Set();
|
|
470
|
+
addSearchTokensFromValue(tokens, content);
|
|
471
|
+
addSearchTokensFromValue(tokens, record?.kind);
|
|
472
|
+
for (const tag of record?.purpose_tags ?? []) addSearchTokensFromValue(tokens, tag);
|
|
473
|
+
return tokens;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
function searchScore(queryTokens, memoryTokens) {
|
|
477
|
+
if (queryTokens.size === 0) return 0;
|
|
478
|
+
let overlap = 0;
|
|
479
|
+
for (const token of queryTokens) {
|
|
480
|
+
if (memoryTokens.has(token)) overlap += 1;
|
|
481
|
+
}
|
|
482
|
+
return Math.round((overlap / queryTokens.size) * 1_000_000) / 1_000_000;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
function searchResultReceiptIds(vault, memoryAddr) {
|
|
486
|
+
return vault.receipts
|
|
487
|
+
.filter((receipt) => receipt?.memory_addr === memoryAddr || receipt?.source_addr === memoryAddr)
|
|
488
|
+
.map((receipt) => receipt.receipt_id)
|
|
489
|
+
.filter((receiptId) => typeof receiptId === 'string' && receiptId.length > 0);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
function publicAccessReceiptRef(receipt) {
|
|
493
|
+
return {
|
|
494
|
+
access_receipt_ref: `enigma://memory-access/${receipt.receipt_id}`,
|
|
495
|
+
receipt_id: receipt.receipt_id,
|
|
496
|
+
operation: receipt.operation,
|
|
497
|
+
memory_addr: receipt.address,
|
|
498
|
+
plan_hash: receipt.plan_hash,
|
|
499
|
+
estimated_prompt_tokens: receipt.estimated_prompt_tokens,
|
|
500
|
+
access_boundary: receipt.access_boundary,
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
function searchCandidates(vault, queryTokens) {
|
|
505
|
+
const candidates = [];
|
|
506
|
+
const byAddress = new Map();
|
|
507
|
+
for (const memoryAddr of [...vault.activeAddresses].sort()) {
|
|
508
|
+
const record = vault.__getRecord(memoryAddr);
|
|
509
|
+
if (!record || record.state !== 'active') continue;
|
|
510
|
+
const content = vault.__getPlaintext(memoryAddr);
|
|
511
|
+
const score = searchScore(queryTokens, recordSearchTokens(record, content));
|
|
512
|
+
if (queryTokens.size > 0 && score === 0) continue;
|
|
513
|
+
const candidate = {
|
|
514
|
+
address: memoryAddr,
|
|
515
|
+
content,
|
|
516
|
+
importance: typeof record.importance === 'number' ? record.importance : typeof record.confidence === 'number' ? record.confidence : undefined,
|
|
517
|
+
last_accessed_at: record.updated_at ?? record.created_at,
|
|
518
|
+
metadata: {
|
|
519
|
+
kind: record.kind,
|
|
520
|
+
sensitivity: record.sensitivity,
|
|
521
|
+
purpose_tags: record.purpose_tags ?? [],
|
|
522
|
+
},
|
|
523
|
+
};
|
|
524
|
+
candidates.push(candidate);
|
|
525
|
+
byAddress.set(memoryAddr, { record, content, score });
|
|
526
|
+
}
|
|
527
|
+
return { candidates, byAddress };
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
function connectorReadinessSummary(bundlePath) {
|
|
531
|
+
return {
|
|
532
|
+
ready: true,
|
|
533
|
+
bundle: bundlePath,
|
|
534
|
+
bundle_env: 'ENIGMA_BUNDLE',
|
|
535
|
+
mcp_command: 'enigma-mcp',
|
|
536
|
+
supported_clients: supportedClients,
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
|
|
276
540
|
function demoBundleRef(bundleWasSupplied) {
|
|
277
541
|
return bundleWasSupplied ? 'supplied_bundle' : DEFAULT_CROSS_MODEL_DEMO_BUNDLE;
|
|
278
542
|
}
|
|
@@ -493,85 +757,380 @@ async function initCommand(flags, io) {
|
|
|
493
757
|
return 0;
|
|
494
758
|
}
|
|
495
759
|
|
|
496
|
-
|
|
760
|
+
function setupClientIds(flags) {
|
|
761
|
+
const raw = getFlag(flags, ['client']);
|
|
762
|
+
if (raw === undefined) return { mode: 'default', auto: false, clients: [...DEFAULT_SETUP_CLIENTS], explicit_clients: [] };
|
|
763
|
+
const values = Array.isArray(raw) ? raw : [raw];
|
|
764
|
+
const clients = [];
|
|
765
|
+
let auto = false;
|
|
766
|
+
for (const value of values) {
|
|
767
|
+
if (value === true || value === '') throw new Error('Missing required --client.');
|
|
768
|
+
for (const client of String(value).split(',').map((item) => item.trim()).filter(Boolean)) {
|
|
769
|
+
if (client === 'auto') {
|
|
770
|
+
auto = true;
|
|
771
|
+
continue;
|
|
772
|
+
}
|
|
773
|
+
getClientProfile(client);
|
|
774
|
+
if (!clients.includes(client)) clients.push(client);
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
if (auto) return { mode: 'auto', auto: true, clients, explicit_clients: clients };
|
|
778
|
+
return { mode: clients.length > 0 ? 'explicit' : 'default', auto: false, clients: clients.length > 0 ? clients : [...DEFAULT_SETUP_CLIENTS], explicit_clients: clients };
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
function setupDetectedClientReason(client) {
|
|
782
|
+
if (client.installed === true && client.recommended_action === 'already_configured') return 'already_configured';
|
|
783
|
+
if (client.installed === true) return 'installed_needs_repair';
|
|
784
|
+
return 'client_config_present';
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
function setupSkippedClientReason(client) {
|
|
788
|
+
if (client.parse_error === true) return 'config_json_invalid';
|
|
789
|
+
if (client.config_path_exists === false || client.exists === false) return 'client_config_missing';
|
|
790
|
+
if (client.ok === false) return 'config_unreadable';
|
|
791
|
+
return 'not_selected';
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
function publicSetupClientSelectionEntry(client, reason) {
|
|
795
|
+
return {
|
|
796
|
+
client_id: client.client_id,
|
|
797
|
+
display_name: client.display_name,
|
|
798
|
+
reason,
|
|
799
|
+
action: client.recommended_action ?? client.action ?? null,
|
|
800
|
+
installed: client.installed === true,
|
|
801
|
+
config_path_exists: client.config_path_exists === true || client.exists === true,
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
async function setupAutoClientSelection(flags, artifacts, fallbackClients, mode) {
|
|
806
|
+
const doctor = await doctorConnectors({
|
|
807
|
+
...connectorOptions(flags),
|
|
808
|
+
bundlePath: artifacts.bundlePath,
|
|
809
|
+
redactPaths: true,
|
|
810
|
+
});
|
|
811
|
+
const detected = doctor.clients.filter((client) => (client.config_path_exists === true || client.exists === true) && client.parse_error !== true);
|
|
812
|
+
const fallbackUsed = detected.length === 0;
|
|
813
|
+
const selectedClients = fallbackUsed ? [...fallbackClients] : detected.map((client) => client.client_id);
|
|
814
|
+
const selectedSet = new Set(selectedClients);
|
|
815
|
+
const detectedSet = new Set(detected.map((client) => client.client_id));
|
|
816
|
+
const selected = fallbackUsed
|
|
817
|
+
? selectedClients.map((clientId) => {
|
|
818
|
+
const client = doctor.clients.find((entry) => entry.client_id === clientId) ?? { client_id: clientId, display_name: getClientProfile(clientId).display_name };
|
|
819
|
+
return publicSetupClientSelectionEntry(client, 'default_fallback_no_client_configs_detected');
|
|
820
|
+
})
|
|
821
|
+
: detected.map((client) => publicSetupClientSelectionEntry(client, setupDetectedClientReason(client)));
|
|
822
|
+
const skipped = doctor.clients
|
|
823
|
+
.filter((client) => !selectedSet.has(client.client_id))
|
|
824
|
+
.map((client) => publicSetupClientSelectionEntry(client, fallbackUsed && !detectedSet.has(client.client_id) ? 'not_in_default_fallback' : setupSkippedClientReason(client)));
|
|
825
|
+
const connectableClientIds = new Set(detected.map((client) => client.client_id));
|
|
826
|
+
return {
|
|
827
|
+
mode,
|
|
828
|
+
auto: true,
|
|
829
|
+
fallback_used: fallbackUsed,
|
|
830
|
+
clients: selectedClients,
|
|
831
|
+
selected,
|
|
832
|
+
skipped,
|
|
833
|
+
connectable_client_ids: connectableClientIds,
|
|
834
|
+
detection: doctor,
|
|
835
|
+
};
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
function setupMemorySource(flags) {
|
|
839
|
+
if (getFlag(flags, ['memory-file', 'memoryFile', 'text-file', 'textFile']) !== undefined) return 'memory_file';
|
|
840
|
+
if (getFlag(flags, ['memory-text', 'memoryText']) !== undefined) return 'demo_text';
|
|
841
|
+
return 'default_demo';
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
function commandPath(path) {
|
|
845
|
+
return `"${String(path).replace(/"/g, '\\"')}"`;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
function publicPathDisplay(path, label) {
|
|
849
|
+
const value = String(path);
|
|
850
|
+
if (/^[A-Za-z]:[\\/]/.test(value) || value.startsWith('/') || value.startsWith('\\\\')) return `<${label}>`;
|
|
851
|
+
return value;
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
function setupPublicDisplays(bundleInput, outDirInput) {
|
|
855
|
+
const outDir = publicPathDisplay(outDirInput, 'out-dir');
|
|
856
|
+
return {
|
|
857
|
+
bundle: publicPathDisplay(bundleInput, 'bundle-path'),
|
|
858
|
+
context_pack: quickstartPathDisplay(outDir, QUICKSTART_ARTIFACT_NAMES.contextPack),
|
|
859
|
+
export: quickstartPathDisplay(outDir, QUICKSTART_ARTIFACT_NAMES.export),
|
|
860
|
+
verify_report: quickstartPathDisplay(outDir, QUICKSTART_ARTIFACT_NAMES.verifyReport),
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
function setupRawDisplays(bundleInput, outDirInput) {
|
|
865
|
+
const plan = quickstartOutputs(bundleInput, outDirInput);
|
|
866
|
+
return {
|
|
867
|
+
bundle: plan.outputs[0].display,
|
|
868
|
+
context_pack: plan.contextPackDisplay,
|
|
869
|
+
export: plan.exportDisplay,
|
|
870
|
+
verify_report: plan.verifyReportDisplay,
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
function publicSetupError(error, rawDisplays, publicDisplays) {
|
|
875
|
+
let message = error.message;
|
|
876
|
+
for (const [raw, safe] of Object.entries({
|
|
877
|
+
[rawDisplays.bundle]: publicDisplays.bundle,
|
|
878
|
+
[rawDisplays.context_pack]: publicDisplays.context_pack,
|
|
879
|
+
[rawDisplays.export]: publicDisplays.export,
|
|
880
|
+
[rawDisplays.verify_report]: publicDisplays.verify_report,
|
|
881
|
+
})) {
|
|
882
|
+
message = message.split(raw).join(safe);
|
|
883
|
+
}
|
|
884
|
+
return new Error(message);
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
function setupNextCommands(bundleInput, exportDisplay, clients, writeConnectors) {
|
|
888
|
+
const primaryClient = clients[0] ?? DEFAULT_SETUP_CLIENTS[0];
|
|
889
|
+
const commands = [
|
|
890
|
+
`enigma remember --bundle ${commandPath(bundleInput)} --text-file ./memory.txt`,
|
|
891
|
+
`enigma search --bundle ${commandPath(bundleInput)} --query "project context"`,
|
|
892
|
+
`enigma context --bundle ${commandPath(bundleInput)} --query "project context"`,
|
|
893
|
+
`enigma verify --export ${commandPath(exportDisplay)}`,
|
|
894
|
+
];
|
|
895
|
+
if (!writeConnectors) commands.push(`enigma connect ${primaryClient} --bundle ${commandPath(bundleInput)}`);
|
|
896
|
+
return commands;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
async function setupDoctorChecks(flags, artifacts, clients, displays) {
|
|
900
|
+
const packageJson = await readPackageJson();
|
|
901
|
+
const requiredNodeMajor = minimumNodeMajor(packageJson.engines?.node);
|
|
902
|
+
const currentNodeMajor = nodeMajor(process.versions.node);
|
|
903
|
+
const binMap = packageJson.bin && typeof packageJson.bin === 'object' && !Array.isArray(packageJson.bin) ? packageJson.bin : {};
|
|
904
|
+
const binEntries = await Promise.all(REQUIRED_PACKAGE_BINS.map(async (name) => {
|
|
905
|
+
const target = binMap[name];
|
|
906
|
+
const declared = typeof target === 'string' && target.length > 0;
|
|
907
|
+
return {
|
|
908
|
+
name,
|
|
909
|
+
target: declared ? target : null,
|
|
910
|
+
declared,
|
|
911
|
+
exists: declared ? await fileExists(packageFileUrl(target)) : false,
|
|
912
|
+
};
|
|
913
|
+
}));
|
|
914
|
+
const schemas = await schemaFiles();
|
|
915
|
+
const connectorBaseOptions = {
|
|
916
|
+
...connectorOptions(flags),
|
|
917
|
+
bundlePath: artifacts.bundlePath,
|
|
918
|
+
redactPaths: true,
|
|
919
|
+
};
|
|
920
|
+
const connectorClients = [];
|
|
921
|
+
for (const client of clients) {
|
|
922
|
+
const doctor = await doctorConnectors({ ...connectorBaseOptions, clientId: client });
|
|
923
|
+
connectorClients.push(...doctor.clients);
|
|
924
|
+
}
|
|
925
|
+
const checks = {
|
|
926
|
+
node: {
|
|
927
|
+
ok: requiredNodeMajor === 0 || currentNodeMajor >= requiredNodeMajor,
|
|
928
|
+
current: process.versions.node,
|
|
929
|
+
required: packageJson.engines?.node ?? null,
|
|
930
|
+
},
|
|
931
|
+
package_bins: {
|
|
932
|
+
ok: binEntries.every((entry) => entry.declared && entry.exists),
|
|
933
|
+
required: REQUIRED_PACKAGE_BINS,
|
|
934
|
+
entries: binEntries,
|
|
935
|
+
missing: binEntries.filter((entry) => !entry.declared).map((entry) => entry.name),
|
|
936
|
+
missing_targets: binEntries.filter((entry) => entry.declared && !entry.exists).map((entry) => entry.name),
|
|
937
|
+
},
|
|
938
|
+
artifacts: {
|
|
939
|
+
ok: artifacts.verifyReport.ok === true,
|
|
940
|
+
bundle: displays.bundle,
|
|
941
|
+
context_pack: displays.context_pack,
|
|
942
|
+
export: displays.export,
|
|
943
|
+
verify_report: displays.verify_report,
|
|
944
|
+
},
|
|
945
|
+
schemas: {
|
|
946
|
+
ok: schemas.length > 0,
|
|
947
|
+
count: schemas.length,
|
|
948
|
+
files: schemas,
|
|
949
|
+
},
|
|
950
|
+
connectors: {
|
|
951
|
+
ok: connectorClients.every((client) => client.ok !== false),
|
|
952
|
+
clients: connectorClients,
|
|
953
|
+
},
|
|
954
|
+
};
|
|
955
|
+
return { ok: Object.values(checks).every((check) => check.ok !== false), checks };
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
function publicConnectPlan(plan, wizard, profile, snippet) {
|
|
959
|
+
const changed = plan?.changed !== false;
|
|
960
|
+
const dryRun = plan?.dryRun === true || plan?.dry_run === true;
|
|
961
|
+
const plannedWrites = changed ? [{ type: 'write', path: wizard.default_config_path }] : [];
|
|
962
|
+
return {
|
|
963
|
+
ok: plan?.ok !== false,
|
|
964
|
+
action: 'connect',
|
|
965
|
+
client_id: profile.client_id,
|
|
966
|
+
configPath: wizard.default_config_path,
|
|
967
|
+
config_path: wizard.default_config_path,
|
|
968
|
+
serverName: profile.server_name,
|
|
969
|
+
server_name: profile.server_name,
|
|
970
|
+
changed,
|
|
971
|
+
dryRun,
|
|
972
|
+
dry_run: dryRun,
|
|
973
|
+
writes_performed: changed && !dryRun,
|
|
974
|
+
backup_planned: Boolean(plan?.backupPath),
|
|
975
|
+
plannedWrites,
|
|
976
|
+
planned_writes: plannedWrites,
|
|
977
|
+
config: snippet,
|
|
978
|
+
};
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
async function setupConnectorPlans(flags, artifacts, clients, writeConnectors, displays, writeClientIds = null) {
|
|
982
|
+
const publicOptions = {
|
|
983
|
+
...connectorOptions(flags),
|
|
984
|
+
bundlePath: displays.bundle,
|
|
985
|
+
};
|
|
986
|
+
const writeOptions = {
|
|
987
|
+
...connectorOptions(flags),
|
|
988
|
+
bundlePath: artifacts.bundlePath,
|
|
989
|
+
};
|
|
990
|
+
const connectors = [];
|
|
991
|
+
for (const client of clients) {
|
|
992
|
+
const profile = getClientProfile(client, publicOptions);
|
|
993
|
+
const snippet = renderMcpConfig(client, publicOptions);
|
|
994
|
+
const wizard = planConnectWizard(client, { platform: profile.platform }).clients[0];
|
|
995
|
+
const writeAllowed = writeClientIds === null || writeClientIds.has(client);
|
|
996
|
+
const rawPlan = writeConnectors && writeAllowed
|
|
997
|
+
? await connectClient(client, { ...writeOptions, dryRun: false })
|
|
998
|
+
: { ok: true, changed: !(writeConnectors && !writeAllowed), dryRun: true };
|
|
999
|
+
const plan = publicConnectPlan(rawPlan, wizard, profile, snippet);
|
|
1000
|
+
connectors.push({
|
|
1001
|
+
client_id: client,
|
|
1002
|
+
display_name: profile.display_name,
|
|
1003
|
+
default_config_path: wizard.default_config_path,
|
|
1004
|
+
mcp_config_snippet: snippet,
|
|
1005
|
+
connect_command: `enigma connect ${client} --bundle ${commandPath(displays.bundle)}`,
|
|
1006
|
+
connect_plan: plan,
|
|
1007
|
+
write_selected: writeAllowed,
|
|
1008
|
+
write_skipped_reason: writeConnectors && !writeAllowed ? 'client_config_missing' : null,
|
|
1009
|
+
wizard,
|
|
1010
|
+
});
|
|
1011
|
+
}
|
|
1012
|
+
return connectors;
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
function publicSetupClientSelection(selection) {
|
|
1016
|
+
return {
|
|
1017
|
+
mode: selection.mode,
|
|
1018
|
+
auto: selection.auto === true,
|
|
1019
|
+
fallback_used: selection.fallback_used === true,
|
|
1020
|
+
selected: selection.selected,
|
|
1021
|
+
skipped: selection.skipped,
|
|
1022
|
+
};
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
export async function setupCommand(flags, io) {
|
|
497
1026
|
const bundleInput = pathFlag(flags, ['bundle', 'file'], DEFAULT_BUNDLE);
|
|
498
1027
|
const outDirInput = pathFlag(flags, ['out-dir', 'outDir'], dirname(bundleInput));
|
|
499
|
-
const
|
|
500
|
-
const
|
|
501
|
-
const
|
|
502
|
-
const
|
|
503
|
-
const
|
|
504
|
-
const
|
|
505
|
-
const
|
|
506
|
-
const
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
{
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
1028
|
+
const requestedSelection = setupClientIds(flags);
|
|
1029
|
+
const overwrite = booleanFlag(flags, ['overwrite'], false);
|
|
1030
|
+
const dryRun = booleanFlag(flags, ['dry-run', 'dryRun'], false);
|
|
1031
|
+
const writeConnectorsFlag = booleanFlag(flags, ['write-connectors', 'writeConnectors'], false);
|
|
1032
|
+
const connectInstalled = booleanFlag(flags, ['connect-installed', 'connectInstalled'], false);
|
|
1033
|
+
const connectorWritesRequested = (writeConnectorsFlag || connectInstalled) && !dryRun;
|
|
1034
|
+
const displays = setupPublicDisplays(bundleInput, outDirInput);
|
|
1035
|
+
const rawDisplays = setupRawDisplays(bundleInput, outDirInput);
|
|
1036
|
+
let artifacts;
|
|
1037
|
+
try {
|
|
1038
|
+
artifacts = await buildQuickstartArtifacts(flags, { bundleInput, outDirInput, overwrite, write: !dryRun });
|
|
1039
|
+
} catch (error) {
|
|
1040
|
+
throw publicSetupError(error, rawDisplays, displays);
|
|
1041
|
+
}
|
|
1042
|
+
const selection = connectInstalled || requestedSelection.auto
|
|
1043
|
+
? await setupAutoClientSelection(flags, artifacts, DEFAULT_SETUP_CLIENTS, connectInstalled ? 'connect_installed' : 'auto')
|
|
1044
|
+
: {
|
|
1045
|
+
...requestedSelection,
|
|
1046
|
+
fallback_used: false,
|
|
1047
|
+
selected: requestedSelection.clients.map((clientId) => {
|
|
1048
|
+
const profile = getClientProfile(clientId);
|
|
1049
|
+
return publicSetupClientSelectionEntry({ client_id: clientId, display_name: profile.display_name }, requestedSelection.mode === 'default' ? 'default_setup_client' : 'explicit_client');
|
|
1050
|
+
}),
|
|
1051
|
+
skipped: [],
|
|
1052
|
+
connectable_client_ids: null,
|
|
1053
|
+
};
|
|
1054
|
+
const clients = selection.clients;
|
|
1055
|
+
const writeClientIds = connectInstalled ? selection.connectable_client_ids : null;
|
|
1056
|
+
const connectors = await setupConnectorPlans(flags, artifacts, clients, connectorWritesRequested, displays, writeClientIds);
|
|
1057
|
+
const doctor = await setupDoctorChecks(flags, artifacts, clients, displays);
|
|
1058
|
+
const ok = artifacts.verifyReport.ok === true;
|
|
1059
|
+
const anyConnectorWritePerformed = connectors.some((connector) => connector.connect_plan.writes_performed === true);
|
|
516
1060
|
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
1061
|
+
print({
|
|
1062
|
+
ok,
|
|
1063
|
+
schema: 'enigma.setup.v1',
|
|
1064
|
+
command: 'enigma setup',
|
|
1065
|
+
dry_run: dryRun,
|
|
1066
|
+
artifacts_written: !dryRun,
|
|
1067
|
+
client_configs_written: writeConnectorsFlag && !dryRun ? true : anyConnectorWritePerformed,
|
|
1068
|
+
client_config_write_requested: connectorWritesRequested,
|
|
1069
|
+
connector_write_mode: connectInstalled ? 'installed_only' : (writeConnectorsFlag ? 'selected_clients' : 'plan_only'),
|
|
1070
|
+
connect_installed: connectInstalled,
|
|
1071
|
+
bundle: displays.bundle,
|
|
1072
|
+
context_pack: displays.context_pack,
|
|
1073
|
+
export: displays.export,
|
|
1074
|
+
verify_report: displays.verify_report,
|
|
1075
|
+
memory_source: setupMemorySource(flags),
|
|
1076
|
+
memory_plaintext_echoed: false,
|
|
1077
|
+
memory_count: Array.isArray(artifacts.bundle.memory_objects) ? artifacts.bundle.memory_objects.length : 0,
|
|
1078
|
+
receipt_count: Array.isArray(artifacts.bundle.receipts) ? artifacts.bundle.receipts.length : 0,
|
|
1079
|
+
context_item_count: Array.isArray(artifacts.contextPack.memories) ? artifacts.contextPack.memories.length : 0,
|
|
1080
|
+
verify_ok: artifacts.verifyReport.ok === true,
|
|
1081
|
+
provider_credentials_required: false,
|
|
1082
|
+
provider_native_memory_canonical: false,
|
|
1083
|
+
selected_clients: clients,
|
|
1084
|
+
skipped_clients: selection.skipped,
|
|
1085
|
+
client_selection: publicSetupClientSelection(selection),
|
|
1086
|
+
connector_write_skips: connectors
|
|
1087
|
+
.filter((connector) => connector.write_skipped_reason)
|
|
1088
|
+
.map((connector) => ({
|
|
1089
|
+
client_id: connector.client_id,
|
|
1090
|
+
display_name: connector.display_name,
|
|
1091
|
+
reason: connector.write_skipped_reason,
|
|
1092
|
+
})),
|
|
1093
|
+
connectors,
|
|
1094
|
+
mcp_config_snippets: Object.fromEntries(connectors.map((connector) => [connector.client_id, connector.mcp_config_snippet])),
|
|
1095
|
+
connect_plans: Object.fromEntries(connectors.map((connector) => [connector.client_id, connector.connect_plan])),
|
|
1096
|
+
next_commands: setupNextCommands(displays.bundle, displays.export, clients, connectorWritesRequested && (!connectInstalled || anyConnectorWritePerformed)),
|
|
1097
|
+
checks: doctor.checks,
|
|
1098
|
+
claim_boundaries: { ...SETUP_CLAIM_BOUNDARIES },
|
|
1099
|
+
}, io);
|
|
1100
|
+
return ok ? 0 : 1;
|
|
1101
|
+
}
|
|
545
1102
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
1103
|
+
export async function quickstartCommand(flags, io) {
|
|
1104
|
+
const bundleInput = pathFlag(flags, ['bundle', 'file'], DEFAULT_BUNDLE);
|
|
1105
|
+
const outDirInput = pathFlag(flags, ['out-dir', 'outDir'], dirname(bundleInput));
|
|
1106
|
+
const overwrite = booleanFlag(flags, ['overwrite'], false);
|
|
1107
|
+
const artifacts = await buildQuickstartArtifacts(flags, { bundleInput, outDirInput, overwrite, write: true });
|
|
550
1108
|
|
|
551
1109
|
print({
|
|
552
|
-
ok: verifyReport.ok === true,
|
|
1110
|
+
ok: artifacts.verifyReport.ok === true,
|
|
553
1111
|
bundle: bundleInput,
|
|
554
|
-
context_pack: contextPackDisplay,
|
|
555
|
-
export: exportDisplay,
|
|
556
|
-
verify_report: verifyReportDisplay,
|
|
557
|
-
memory_count: Array.isArray(bundle.memory_objects) ? bundle.memory_objects.length : 0,
|
|
558
|
-
receipt_count: Array.isArray(bundle.receipts) ? bundle.receipts.length : 0,
|
|
559
|
-
context_item_count: Array.isArray(contextPack.memories) ? contextPack.memories.length : 0,
|
|
560
|
-
verify_ok: verifyReport.ok === true,
|
|
1112
|
+
context_pack: artifacts.contextPackDisplay,
|
|
1113
|
+
export: artifacts.exportDisplay,
|
|
1114
|
+
verify_report: artifacts.verifyReportDisplay,
|
|
1115
|
+
memory_count: Array.isArray(artifacts.bundle.memory_objects) ? artifacts.bundle.memory_objects.length : 0,
|
|
1116
|
+
receipt_count: Array.isArray(artifacts.bundle.receipts) ? artifacts.bundle.receipts.length : 0,
|
|
1117
|
+
context_item_count: Array.isArray(artifacts.contextPack.memories) ? artifacts.contextPack.memories.length : 0,
|
|
1118
|
+
verify_ok: artifacts.verifyReport.ok === true,
|
|
561
1119
|
next_commands: [
|
|
562
|
-
`enigma verify --export ${exportDisplay}`,
|
|
1120
|
+
`enigma verify --export ${artifacts.exportDisplay}`,
|
|
563
1121
|
`enigma connect generic-mcp --bundle ${bundleInput} --dry-run`,
|
|
564
1122
|
],
|
|
565
1123
|
claim_boundaries: {
|
|
566
1124
|
local_only: true,
|
|
567
1125
|
provider_credentials_required: false,
|
|
1126
|
+
provider_native_memory_canonical: false,
|
|
568
1127
|
provider_deletion_proof: false,
|
|
569
1128
|
model_forgetting_proof: false,
|
|
570
1129
|
roi_or_savings_guarantee: false,
|
|
571
1130
|
compliance_certification: false,
|
|
572
1131
|
},
|
|
573
1132
|
}, io);
|
|
574
|
-
return verifyReport.ok === true ? 0 : 1;
|
|
1133
|
+
return artifacts.verifyReport.ok === true ? 0 : 1;
|
|
575
1134
|
}
|
|
576
1135
|
|
|
577
1136
|
export async function crossModelDemoCommand(flags, io) {
|
|
@@ -767,6 +1326,118 @@ async function contextCommand(flags, io) {
|
|
|
767
1326
|
return 0;
|
|
768
1327
|
}
|
|
769
1328
|
|
|
1329
|
+
|
|
1330
|
+
async function searchCommand(flags, io) {
|
|
1331
|
+
const bundlePath = resolve(String(getFlag(flags, ['bundle', 'file'], DEFAULT_BUNDLE)));
|
|
1332
|
+
const query = String(requireFlag(flags, ['query', 'q'], 'query'));
|
|
1333
|
+
const limit = integerFlag(flags, ['limit'], 'limit', 8);
|
|
1334
|
+
if (limit < 0) throw new Error('--limit must be non-negative.');
|
|
1335
|
+
const includeContent = getFlag(flags, ['include-content', 'includeContent']) === true || getFlag(flags, ['include-content', 'includeContent']) === 'true';
|
|
1336
|
+
const { vault } = await loadState(bundlePath);
|
|
1337
|
+
const roots = vault.__computeRoots();
|
|
1338
|
+
const queryTokens = searchTokensFrom(query);
|
|
1339
|
+
const { candidates, byAddress } = searchCandidates(vault, queryTokens);
|
|
1340
|
+
const plan = createMemoryOptimizationPlan({
|
|
1341
|
+
candidates,
|
|
1342
|
+
prompt: query,
|
|
1343
|
+
now: getFlag(flags, ['now'], '2026-01-01T00:00:00.000Z'),
|
|
1344
|
+
});
|
|
1345
|
+
const planIndex = new Map(plan.items.map((item, index) => [item.address, index]));
|
|
1346
|
+
const selectedItems = plan.items
|
|
1347
|
+
.filter((item) => byAddress.has(item.address))
|
|
1348
|
+
.sort((left, right) => {
|
|
1349
|
+
const scoreDiff = byAddress.get(right.address).score - byAddress.get(left.address).score;
|
|
1350
|
+
if (scoreDiff !== 0) return scoreDiff;
|
|
1351
|
+
return planIndex.get(left.address) - planIndex.get(right.address);
|
|
1352
|
+
})
|
|
1353
|
+
.slice(0, limit);
|
|
1354
|
+
const accessReceipts = selectedItems.map((item, index) => createMemoryAccessReceipt({
|
|
1355
|
+
item,
|
|
1356
|
+
plan,
|
|
1357
|
+
sequence: index,
|
|
1358
|
+
timestamp: null,
|
|
1359
|
+
pricing: plan.pricing,
|
|
1360
|
+
}));
|
|
1361
|
+
const accessReceiptByAddress = new Map(accessReceipts.map((receipt) => [receipt.address, publicAccessReceiptRef(receipt)]));
|
|
1362
|
+
const results = selectedItems.map((item) => {
|
|
1363
|
+
const hit = byAddress.get(item.address);
|
|
1364
|
+
const record = hit.record;
|
|
1365
|
+
const accessReceipt = accessReceiptByAddress.get(item.address);
|
|
1366
|
+
return {
|
|
1367
|
+
memory_ref: `enigma://memory/${item.address}`,
|
|
1368
|
+
memory_addr: item.address,
|
|
1369
|
+
address: item.address,
|
|
1370
|
+
kind: record.kind,
|
|
1371
|
+
sensitivity: record.sensitivity,
|
|
1372
|
+
tags: Array.isArray(record.purpose_tags) ? [...record.purpose_tags] : [],
|
|
1373
|
+
purpose_tags: Array.isArray(record.purpose_tags) ? [...record.purpose_tags] : [],
|
|
1374
|
+
score: hit.score,
|
|
1375
|
+
tier: item.tier,
|
|
1376
|
+
receipt_ids: searchResultReceiptIds(vault, item.address),
|
|
1377
|
+
access_receipt_ref: accessReceipt?.access_receipt_ref,
|
|
1378
|
+
access_receipt_id: accessReceipt?.receipt_id,
|
|
1379
|
+
access_receipt_refs: accessReceipt?.access_receipt_ref ? [accessReceipt.access_receipt_ref] : [],
|
|
1380
|
+
content_redacted: !includeContent,
|
|
1381
|
+
...(includeContent ? { content: hit.content } : {}),
|
|
1382
|
+
};
|
|
1383
|
+
});
|
|
1384
|
+
print({
|
|
1385
|
+
ok: true,
|
|
1386
|
+
schema: 'enigma.memory_search.v1',
|
|
1387
|
+
bundle: bundlePath,
|
|
1388
|
+
query_redacted: true,
|
|
1389
|
+
limit,
|
|
1390
|
+
result_count: results.length,
|
|
1391
|
+
results,
|
|
1392
|
+
access_receipts: accessReceipts.map(publicAccessReceiptRef),
|
|
1393
|
+
active_set_root: roots.active_set_root,
|
|
1394
|
+
receipt_log_root: roots.receipt_log_root,
|
|
1395
|
+
claim_boundary: includeContent
|
|
1396
|
+
? 'Search ran against the selected local bundle and includes plaintext only because --include-content was explicit; this does not prove provider deletion, provider-native memory state, or model forgetting.'
|
|
1397
|
+
: 'Search ran against the selected local bundle and redacts plaintext by default; refs, scores, tags, roots, and receipt refs are not provider deletion proof or model forgetting proof.',
|
|
1398
|
+
}, io);
|
|
1399
|
+
return 0;
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
async function statusCommand(flags, io) {
|
|
1403
|
+
const bundlePath = resolve(String(getFlag(flags, ['bundle', 'file'], DEFAULT_BUNDLE)));
|
|
1404
|
+
const { stored, vault, passport } = await loadState(bundlePath);
|
|
1405
|
+
const roots = vault.__computeRoots();
|
|
1406
|
+
const activeCount = activeMemoryCount(vault);
|
|
1407
|
+
const tombstoneCount = vault.tombstones instanceof Map ? vault.tombstones.size : 0;
|
|
1408
|
+
const receiptCount = Array.isArray(vault.receipts) ? vault.receipts.length : 0;
|
|
1409
|
+
print({
|
|
1410
|
+
ok: true,
|
|
1411
|
+
schema: 'enigma.passport_status.v1',
|
|
1412
|
+
bundle: bundlePath,
|
|
1413
|
+
passport_ref: `enigma://passport/${passport.passport_id}`,
|
|
1414
|
+
owner: {
|
|
1415
|
+
subject_id: stored.owner?.subject_id ?? stored.passport?.owner?.subject_id ?? stored.vault?.subject_id ?? passport.owner?.subject_id ?? vault.subject_id,
|
|
1416
|
+
display_name: stored.owner?.display_name ?? stored.passport?.owner?.display_name ?? stored.vault?.display_name ?? passport.owner?.display_name ?? 'Local user',
|
|
1417
|
+
},
|
|
1418
|
+
counts: {
|
|
1419
|
+
active_memories: activeCount,
|
|
1420
|
+
tombstoned_memories: tombstoneCount,
|
|
1421
|
+
receipts: receiptCount,
|
|
1422
|
+
},
|
|
1423
|
+
active_memory_count: activeCount,
|
|
1424
|
+
tombstoned_memory_count: tombstoneCount,
|
|
1425
|
+
receipt_count: receiptCount,
|
|
1426
|
+
active_set_root: roots.active_set_root,
|
|
1427
|
+
receipt_log_root: roots.receipt_log_root,
|
|
1428
|
+
connector_readiness: connectorReadinessSummary(bundlePath),
|
|
1429
|
+
next_recommended_commands: [
|
|
1430
|
+
`enigma remember --bundle "${bundlePath}" --text-file <path>`,
|
|
1431
|
+
`enigma search --bundle "${bundlePath}" --query <text>`,
|
|
1432
|
+
`enigma context --bundle "${bundlePath}" --query <text>`,
|
|
1433
|
+
`enigma verify --bundle "${bundlePath}"`,
|
|
1434
|
+
`enigma connect <client> --bundle "${bundlePath}"`,
|
|
1435
|
+
],
|
|
1436
|
+
claim_boundary: 'Status reports local bundle counters, owner display fields, connector readiness hints, and commitment roots only; it does not expose raw memory, certify compliance, prove provider deletion, or prove model forgetting.',
|
|
1437
|
+
}, io);
|
|
1438
|
+
return 0;
|
|
1439
|
+
}
|
|
1440
|
+
|
|
770
1441
|
async function exportCommand(flags, io) {
|
|
771
1442
|
const bundlePath = resolve(String(getFlag(flags, ['bundle', 'file'], DEFAULT_BUNDLE)));
|
|
772
1443
|
const { vault } = await loadState(bundlePath);
|
|
@@ -1272,6 +1943,7 @@ function usage() {
|
|
|
1272
1943
|
usage: 'enigma <command> [options]',
|
|
1273
1944
|
commands: [
|
|
1274
1945
|
'init',
|
|
1946
|
+
'setup',
|
|
1275
1947
|
'quickstart',
|
|
1276
1948
|
'demo cross-model',
|
|
1277
1949
|
'doctor',
|
|
@@ -1283,6 +1955,9 @@ function usage() {
|
|
|
1283
1955
|
'update',
|
|
1284
1956
|
'delete',
|
|
1285
1957
|
'context',
|
|
1958
|
+
'search',
|
|
1959
|
+
'status',
|
|
1960
|
+
'passport status',
|
|
1286
1961
|
'export',
|
|
1287
1962
|
'import <source>',
|
|
1288
1963
|
'capsule export',
|
|
@@ -1318,6 +1993,28 @@ function usage() {
|
|
|
1318
1993
|
'--text <text>': 'Inline local memory text. Avoid for private content because argv can be logged by process tooling.',
|
|
1319
1994
|
'--text-file <path>': 'Read local memory text from a file so private smoke input is not exposed in shell argv. Aliases: --memory-file, --textFile, --memoryFile.',
|
|
1320
1995
|
},
|
|
1996
|
+
search_options: {
|
|
1997
|
+
'--query <text>': 'Required local query. Output redacts the query and memory plaintext by default. Alias: --q.',
|
|
1998
|
+
'--bundle <path>': 'Bundle JSON to search. Defaults to .enigma/bundle.json.',
|
|
1999
|
+
'--limit <n>': 'Maximum ranked active memories to return. Defaults to 8.',
|
|
2000
|
+
'--json': 'Reserved for explicit JSON output; CLI output is JSON by default.',
|
|
2001
|
+
'--include-content': 'Opt in to returning plaintext local memory content in the JSON result.',
|
|
2002
|
+
},
|
|
2003
|
+
status_options: {
|
|
2004
|
+
'enigma status --bundle <path>': 'Show local Memory Passport counts, roots, owner display fields, connector readiness, and next commands.',
|
|
2005
|
+
'enigma passport status --bundle <path>': 'Alias for enigma status.',
|
|
2006
|
+
},
|
|
2007
|
+
setup_options: {
|
|
2008
|
+
'--bundle <path>': 'Bundle JSON to create. Defaults to .enigma/bundle.json.',
|
|
2009
|
+
'--out-dir <path>': 'Directory for context-pack.json, export.json, and verify-report.json. Defaults to the bundle directory.',
|
|
2010
|
+
'--client <id|auto>': `Client to plan; repeat or comma-separate. Use auto to plan installed/config-present clients, falling back to ${DEFAULT_SETUP_CLIENTS.join(', ')}.`,
|
|
2011
|
+
'--connect-installed': 'Auto-select installed/config-present clients and write only those existing client configs; missing client configs are reported and skipped.',
|
|
2012
|
+
'--memory-file <path>': 'Read local memory text from a file without echoing plaintext. Alias: --text-file.',
|
|
2013
|
+
'--memory-text <text>': 'Inline demo-only memory text. Avoid for private content because argv can be logged.',
|
|
2014
|
+
'--overwrite': 'Replace existing local setup artifacts.',
|
|
2015
|
+
'--dry-run': 'Plan setup without writing local artifacts or client configs.',
|
|
2016
|
+
'--write-connectors': 'Also write selected client MCP config files. Defaults to false.',
|
|
2017
|
+
},
|
|
1321
2018
|
quickstart_options: {
|
|
1322
2019
|
'--bundle <path>': 'Bundle JSON to create. Defaults to .enigma/bundle.json.',
|
|
1323
2020
|
'--out-dir <path>': 'Directory for context-pack.json, export.json, and verify-report.json. Defaults to the bundle directory.',
|
|
@@ -1398,15 +2095,16 @@ export async function main(argv = process.argv.slice(2), io = { stdout: process.
|
|
|
1398
2095
|
print(usage(), io);
|
|
1399
2096
|
return 0;
|
|
1400
2097
|
}
|
|
1401
|
-
const twoPartCommands = ['boundary', 'mcp', 'mesh', 'enterprise', 'capsule', 'relay', 'gateway', 'connect', 'disconnect', 'import', 'native-host', 'meter', 'settlement', 'demo'];
|
|
2098
|
+
const twoPartCommands = ['boundary', 'mcp', 'mesh', 'enterprise', 'capsule', 'relay', 'gateway', 'connect', 'disconnect', 'import', 'native-host', 'meter', 'settlement', 'demo', 'passport'];
|
|
1402
2099
|
const flags = parseArgs(twoPartCommands.includes(command) ? argv.slice(2) : argv.slice(1));
|
|
1403
2100
|
const positionalFile = optionalPositional(argv[2]);
|
|
1404
|
-
if ((flags.has('help') || argv.includes('-h')) && (((command === 'relay' || command === 'gateway') && (subcommand === 'serve' || subcommand === 'demo')) || (command === 'native-host' && (subcommand === 'manifest' || subcommand === 'install-plan')) || (command === 'demo' && subcommand === 'cross-model'))) {
|
|
2101
|
+
if ((flags.has('help') || argv.includes('-h')) && (command === 'setup' || command === 'search' || command === 'status' || (command === 'passport' && subcommand === 'status') || ((command === 'relay' || command === 'gateway') && (subcommand === 'serve' || subcommand === 'demo')) || (command === 'native-host' && (subcommand === 'manifest' || subcommand === 'install-plan')) || (command === 'demo' && subcommand === 'cross-model'))) {
|
|
1405
2102
|
print(usage(), io);
|
|
1406
2103
|
return 0;
|
|
1407
2104
|
}
|
|
1408
2105
|
try {
|
|
1409
2106
|
if (command === 'init') return await initCommand(flags, io);
|
|
2107
|
+
if (command === 'setup') return await setupCommand(flags, io);
|
|
1410
2108
|
if (command === 'quickstart') return await quickstartCommand(flags, io);
|
|
1411
2109
|
if (command === 'demo' && subcommand === 'cross-model') return await crossModelDemoCommand(flags, io);
|
|
1412
2110
|
if (command === 'doctor') return await doctorCommand(flags, io);
|
|
@@ -1418,6 +2116,9 @@ export async function main(argv = process.argv.slice(2), io = { stdout: process.
|
|
|
1418
2116
|
if (command === 'update') return await updateCommand(flags, io);
|
|
1419
2117
|
if (command === 'delete') return await deleteCommand(flags, io);
|
|
1420
2118
|
if (command === 'context') return await contextCommand(flags, io);
|
|
2119
|
+
if (command === 'search') return await searchCommand(flags, io);
|
|
2120
|
+
if (command === 'status') return await statusCommand(flags, io);
|
|
2121
|
+
if (command === 'passport' && subcommand === 'status') return await statusCommand(flags, io);
|
|
1421
2122
|
if (command === 'export') return await exportCommand(flags, io);
|
|
1422
2123
|
if (command === 'import') return await importCommand(subcommand, flags, io, positionalFile);
|
|
1423
2124
|
if (command === 'capsule' && subcommand === 'export') return await capsuleExportCommand(flags, io, positionalFile);
|
|
@@ -2,6 +2,41 @@
|
|
|
2
2
|
|
|
3
3
|
Enigma Memory is a local-first SDK, CLI, MCP server, and service-contract package. The developer surfaces are designed to be copied without secrets, cloud credentials, hidden local paths, or account identifiers.
|
|
4
4
|
|
|
5
|
+
## CLI memory passport loop
|
|
6
|
+
|
|
7
|
+
For most developers, start with the installed CLI before reading the SDK internals or service contracts:
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm install -g enigma-memory
|
|
11
|
+
enigma setup --overwrite
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
`enigma setup --overwrite` is the safe default. It writes local Enigma artifacts under the workspace `.enigma` path and emits deterministic, public-safe JSON without printing raw memory plaintext. It does not write third-party app configs.
|
|
15
|
+
|
|
16
|
+
To let setup auto-detect installed or already-configured clients and report the connector plan without mutating client configs:
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
enigma setup --client auto --overwrite
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
To explicitly write connector entries for installed/config-present clients only:
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
enigma setup --connect-installed --overwrite
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
`--client auto` selects clients found by connector detection and falls back to the default setup client list when none are present. The setup output lists selected clients, skipped clients, and skip reasons. `--connect-installed` implies auto selection, writes only for installed/config-present clients, and skips missing configs instead of creating every default client config. Only explicit write flags mutate client configs; `enigma connect <client>` without `--dry-run` remains the single-client write path, and existing `enigma setup --write-connectors` behavior for explicit/default clients is unchanged. Treat provider-native memory as non-canonical cache only; the local Enigma vault is canonical.
|
|
29
|
+
|
|
30
|
+
After setup, use the same local vault from the CLI or connected clients:
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
enigma remember --text-file ./memory.txt
|
|
34
|
+
enigma search --query "..."
|
|
35
|
+
enigma context --query "..." --optimize
|
|
36
|
+
enigma verify --export ./.enigma/export.json
|
|
37
|
+
enigma connect claude-desktop --dry-run
|
|
38
|
+
```
|
|
39
|
+
|
|
5
40
|
## Copyable starting points
|
|
6
41
|
|
|
7
42
|
- SDK/API guide: [`docs/sdk-api.md`](./sdk-api.md)
|
|
@@ -25,11 +60,11 @@ The example app prints ids, counts, roots, and verification status only. It does
|
|
|
25
60
|
|
|
26
61
|
## CLI and CI loop
|
|
27
62
|
|
|
28
|
-
The CI example installs Node 24, installs the published `enigma-memory@0.1.
|
|
63
|
+
The CI example installs Node 24, installs the published `enigma-memory@0.1.7` package, runs:
|
|
29
64
|
|
|
30
65
|
```sh
|
|
31
|
-
npx enigma
|
|
32
|
-
npx enigma doctor
|
|
66
|
+
npx --yes --package enigma-memory enigma setup --overwrite
|
|
67
|
+
npx --yes --package enigma-memory enigma doctor
|
|
33
68
|
npm run benchmark:memory-suite -- --out benchmark-report.json
|
|
34
69
|
```
|
|
35
70
|
|
|
@@ -49,22 +84,73 @@ Use the workflow as a template in a consumer repository. It is intentionally lim
|
|
|
49
84
|
|
|
50
85
|
## MCP client loop
|
|
51
86
|
|
|
52
|
-
The generic MCP
|
|
87
|
+
The same installed package can be used by Claude Desktop, Cursor, Kimi Code, or any generic MCP client. The smooth setup path is `enigma setup --client auto --overwrite` to plan detected clients, then `enigma setup --connect-installed --overwrite` only when you explicitly want setup to write installed/config-present client configs. Manual snippets remain useful when a client needs a copied entry; replace the bundle path with the local path from your setup output, and restart the client.
|
|
88
|
+
|
|
89
|
+
Claude Desktop:
|
|
90
|
+
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"mcpServers": {
|
|
94
|
+
"enigma": {
|
|
95
|
+
"command": "enigma-mcp",
|
|
96
|
+
"args": [],
|
|
97
|
+
"env": {
|
|
98
|
+
"ENIGMA_BUNDLE": "/absolute/path/to/.enigma/bundle.json"
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Cursor:
|
|
106
|
+
|
|
107
|
+
```json
|
|
108
|
+
{
|
|
109
|
+
"mcpServers": {
|
|
110
|
+
"enigma": {
|
|
111
|
+
"command": "enigma-mcp",
|
|
112
|
+
"args": [],
|
|
113
|
+
"env": {
|
|
114
|
+
"ENIGMA_BUNDLE": "/absolute/path/to/.enigma/bundle.json"
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Kimi Code:
|
|
122
|
+
|
|
123
|
+
```json
|
|
124
|
+
{
|
|
125
|
+
"mcpServers": {
|
|
126
|
+
"enigma": {
|
|
127
|
+
"command": "enigma-mcp",
|
|
128
|
+
"args": [],
|
|
129
|
+
"env": {
|
|
130
|
+
"ENIGMA_BUNDLE": "/absolute/path/to/.enigma/bundle.json"
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Generic MCP:
|
|
53
138
|
|
|
54
139
|
```json
|
|
55
140
|
{
|
|
56
141
|
"mcpServers": {
|
|
57
142
|
"enigma": {
|
|
58
143
|
"command": "enigma-mcp",
|
|
144
|
+
"args": [],
|
|
59
145
|
"env": {
|
|
60
|
-
"ENIGMA_BUNDLE": "
|
|
146
|
+
"ENIGMA_BUNDLE": "/absolute/path/to/.enigma/bundle.json"
|
|
61
147
|
}
|
|
62
148
|
}
|
|
63
149
|
}
|
|
64
150
|
}
|
|
65
151
|
```
|
|
66
152
|
|
|
67
|
-
|
|
153
|
+
Do not commit private bundle paths if they reveal local usernames, workspace names, account ids, or other personal details.
|
|
68
154
|
|
|
69
155
|
## Claim boundaries for developers
|
|
70
156
|
|
package/docs/sdk-api.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# SDK and API guide
|
|
2
2
|
|
|
3
|
-
This guide covers the public package imports for `enigma-memory@0.1.
|
|
3
|
+
This guide covers the public package imports for `enigma-memory@0.1.7`. The SDK runs locally by default: vaults, passports, context packs, receipts, relay/gateway demo state, storage contracts, metering artifacts, and settlement artifacts are package-level developer surfaces. They are not evidence of hosted Enigma cloud, provider-side deletion, provider model forgetting, token ROI, invoice savings, compliance certification, or benchmark leadership.
|
|
4
4
|
|
|
5
5
|
## Install and import style
|
|
6
6
|
|
package/package.json
CHANGED
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
const DEFAULT_BUNDLE = '.enigma/bundle.json';
|
|
18
18
|
const JSONRPC_VERSION = '2.0';
|
|
19
19
|
const MCP_PROTOCOL_VERSION = '2024-11-05';
|
|
20
|
-
const SERVER_INFO = Object.freeze({ name: 'enigma-mcp-server', version: '0.1.
|
|
20
|
+
const SERVER_INFO = Object.freeze({ name: 'enigma-mcp-server', version: '0.1.7' });
|
|
21
21
|
const JSON_RPC_ID_PATTERN = /^[A-Za-z0-9._:-]{1,128}$/;
|
|
22
22
|
const JSON_RPC_ERROR = Object.freeze({
|
|
23
23
|
INVALID_REQUEST: -32600,
|
|
@@ -6,7 +6,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
6
6
|
|
|
7
7
|
export const INSTALLER_ASSET_SCHEMA = 'enigma.installer_assets.v1';
|
|
8
8
|
export const INSTALLER_ASSET_PACKAGE = 'enigma-memory';
|
|
9
|
-
export const INSTALLER_ASSET_VERSION = '0.1.
|
|
9
|
+
export const INSTALLER_ASSET_VERSION = '0.1.7';
|
|
10
10
|
export const INSTALLER_ASSET_GENERATED_AT = '1970-01-01T00:00:00.000Z';
|
|
11
11
|
|
|
12
12
|
const SCRIPT_PATH = fileURLToPath(import.meta.url);
|
|
@@ -995,7 +995,7 @@ function buildSuiteReport(datasetRows, topK, options) {
|
|
|
995
995
|
generated_at: options.generated_at ?? new Date().toISOString(),
|
|
996
996
|
package: {
|
|
997
997
|
name: 'enigma-memory',
|
|
998
|
-
version: '0.1.
|
|
998
|
+
version: '0.1.7',
|
|
999
999
|
},
|
|
1000
1000
|
public_safe: true,
|
|
1001
1001
|
top_k: topK,
|