@memorax/memorax-code 0.1.4 → 0.1.6
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 +8 -19
- package/bin/memorax-code-npm-preinstall.mjs +28 -39
- package/bin/memorax-code-plugin-postinstall.mjs +23 -1537
- package/bin/memorax-code-setup.mjs +1615 -0
- package/bin/memorax-code.mjs +289 -5
- package/docs/configuration.md +66 -17
- package/docs/troubleshooting.md +105 -7
- package/lib/memorax-code-adapter-common/src/config-utils.d.mts +5 -1
- package/lib/memorax-code-adapter-common/src/config-utils.mjs +36 -3
- package/lib/memorax-code-adapter-common/src/credentials/linux-secret-service.mjs +110 -0
- package/lib/memorax-code-adapter-common/src/credentials/macos-keychain.mjs +224 -0
- package/lib/memorax-code-adapter-common/src/credentials/secure-command.mjs +340 -0
- package/lib/memorax-code-adapter-common/src/credentials/trial-credential-record.d.mts +82 -0
- package/lib/memorax-code-adapter-common/src/credentials/trial-credential-record.mjs +183 -0
- package/lib/memorax-code-adapter-common/src/credentials/trial-credential-store.d.mts +82 -0
- package/lib/memorax-code-adapter-common/src/credentials/trial-credential-store.mjs +271 -0
- package/lib/memorax-code-adapter-common/src/credentials/windows-dpapi.mjs +440 -0
- package/lib/memorax-code-adapter-common/src/hooks/memory-skill-reminder-hook.mjs +7 -4
- package/lib/memorax-code-adapter-common/src/setup-completion.d.mts +81 -0
- package/lib/memorax-code-adapter-common/src/setup-completion.mjs +154 -0
- package/lib/memorax-code-backend/dist/clients/claude/memory-hook-runtime.js +21 -2
- package/lib/memorax-code-backend/dist/clients/codex/memory-hook-runtime.js +23 -3
- package/lib/memorax-code-backend/dist/clients/opencode/memory-hook-runtime.js +19 -1
- package/lib/memorax-code-backend/dist/entrypoints/backend-cli.js +17 -16
- package/lib/memorax-code-backend/dist/lifecycle/backend/process.js +21 -0
- package/lib/memorax-code-backend/dist/lifecycle/orchestrator.js +81 -22
- package/lib/memorax-code-backend/dist/memorax-cli.js +3 -0
- package/lib/memorax-code-backend/dist/memory/automatic-retrieval.js +8 -2
- package/lib/memorax-code-backend/dist/memory/automatic-writeback.js +13 -1
- package/lib/memorax-code-backend/dist/memory/cli.js +10 -0
- package/lib/memorax-code-backend/dist/memory/quota-notice.js +208 -0
- package/lib/memorax-code-backend/dist/memory/service.js +11 -0
- package/lib/memorax-code-backend/dist/provider/memorax/adapter.js +4 -2
- package/lib/memorax-code-backend/dist/provider/memorax/http.js +10 -3
- package/lib/memorax-code-backend/dist/provider/memorax/quota.js +26 -0
- package/lib/memorax-code-backend/package.json +2 -2
- package/lib/memorax-code-claude-adapter/.claude-plugin/plugin.json +1 -1
- package/lib/memorax-code-claude-adapter/hooks/runtime-shell.json +1 -1
- package/lib/memorax-code-claude-adapter/package.json +1 -1
- package/lib/memorax-code-claude-adapter/runtime-hooks/memory-turn.mjs +5 -3
- package/lib/memorax-code-claude-adapter/skills/memorax-code/references/memorax-add.md +2 -0
- package/lib/memorax-code-claude-adapter/skills/memorax-code/references/memorax-search.md +4 -2
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/.claude-plugin/plugin.json +1 -1
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/hooks/runtime-shell.json +1 -1
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/config-utils.d.mts +5 -1
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/config-utils.mjs +36 -3
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/credentials/linux-secret-service.mjs +110 -0
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/credentials/macos-keychain.mjs +224 -0
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/credentials/secure-command.mjs +340 -0
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/credentials/trial-credential-record.d.mts +82 -0
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/credentials/trial-credential-record.mjs +183 -0
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/credentials/trial-credential-store.d.mts +82 -0
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/credentials/trial-credential-store.mjs +271 -0
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/credentials/windows-dpapi.mjs +440 -0
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/hooks/memory-skill-reminder-hook.mjs +7 -4
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/setup-completion.d.mts +81 -0
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/setup-completion.mjs +154 -0
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/package.json +1 -1
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/runtime-hooks/memory-turn.mjs +5 -3
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/references/memorax-add.md +2 -0
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/references/memorax-search.md +4 -2
- package/lib/memorax-code-codex-adapter/.codex-plugin/plugin.json +1 -1
- package/lib/memorax-code-codex-adapter/hooks/runtime-shell.json +1 -1
- package/lib/memorax-code-codex-adapter/package.json +1 -1
- package/lib/memorax-code-codex-adapter/runtime-hooks/memory-skill-reminder.mjs +2 -0
- package/lib/memorax-code-codex-adapter/skills/memorax-code/references/memorax-add.md +2 -0
- package/lib/memorax-code-codex-adapter/skills/memorax-code/references/memorax-search.md +4 -2
- package/lib/memorax-code-dsh-adapter/hooks/repo-memory-job.mjs +5 -2
- package/lib/memorax-code-dsh-adapter/package.json +1 -1
- package/lib/memorax-code-dsh-adapter/skills/memorax-code/references/memorax-add.md +2 -0
- package/lib/memorax-code-dsh-adapter/skills/memorax-code/references/memorax-search.md +4 -2
- package/lib/memorax-code-dsh-adapter/src/profile-lifecycle.mjs +158 -7
- package/lib/memorax-code-dsh-adapter/src/runtime-state.mjs +21 -0
- package/lib/memorax-code-opencode-adapter/package.json +1 -1
- package/lib/memorax-code-opencode-adapter/skills/memorax-code/references/memorax-add.md +2 -0
- package/lib/memorax-code-opencode-adapter/skills/memorax-code/references/memorax-search.md +4 -2
- package/lib/memorax-code-opencode-adapter/src/plugin.mjs +20 -0
- package/lib/node-version.mjs +1 -1
- package/lib/package-transition.mjs +311 -0
- package/lib/resolve-codex-command.mjs +32 -6
- package/lib/setup-memory-preferences.mjs +113 -0
- package/lib/setup-reconcile.mjs +128 -0
- package/lib/trial-plugin-mark.mjs +171 -0
- package/lib/trial-provision-client.mjs +307 -0
- package/lib/trial-provision-flow.mjs +215 -0
- package/lib/trial-setup.mjs +53 -0
- package/package.json +2 -2
|
@@ -1,1552 +1,38 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { spawnSync } from "node:child_process";
|
|
3
|
-
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
4
2
|
import { homedir } from "node:os";
|
|
5
|
-
import { dirname, join
|
|
6
|
-
import { createInterface } from "node:readline/promises";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
7
4
|
import { fileURLToPath } from "node:url";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
setTomlField,
|
|
11
|
-
updateConfigFileAtomically,
|
|
12
|
-
} from "../lib/memorax-code-adapter-common/src/memorax-code-config-file.mjs";
|
|
13
|
-
import {
|
|
14
|
-
MEMORAX_ACCOUNT_URL,
|
|
15
|
-
MEMORAX_DEFAULT_BASE_URL,
|
|
16
|
-
MEMORAX_DEFAULT_MEMORY_OUTPUT_LANGUAGE,
|
|
17
|
-
normalizeMemoraxBaseUrl,
|
|
18
|
-
normalizeMemoraxMemoryOutputLanguage,
|
|
19
|
-
} from "../lib/memorax-code-adapter-common/src/memorax-defaults.mjs";
|
|
20
|
-
import { stagePackagedClientHookRuntime } from "../lib/client-hook-runtime.mjs";
|
|
21
|
-
import { discoverDshProfiles } from "../lib/dsh-plugin-install.mjs";
|
|
22
|
-
import { ensureClaudeCommandEnv } from "../lib/resolve-claude-command.mjs";
|
|
23
|
-
import { ensureCodexCommandEnv } from "../lib/resolve-codex-command.mjs";
|
|
24
|
-
import { commandOnPath } from "../lib/vscode-extension-command.mjs";
|
|
25
|
-
import { resolveWindowsCliInvocation } from "../lib/windows-cli-invocation.mjs";
|
|
5
|
+
import { unsupportedNodeVersionMessage } from "../lib/node-version.mjs";
|
|
6
|
+
import { runNpmPostinstallPackageTransition } from "../lib/package-transition.mjs";
|
|
26
7
|
|
|
27
|
-
const PLUGIN_NAME = "memorax-code-codex-adapter";
|
|
28
|
-
const CLI_MARKETPLACE_NAME = "memorax-code";
|
|
29
|
-
const PERSONAL_MARKETPLACE_NAME = "personal";
|
|
30
|
-
const PREVIOUS_HOOKS_ENV = "MEMORAX_CODE_CODEX_PREVIOUS_HOOKS_JSON";
|
|
31
|
-
const HOOK_TRUST_SELECTION_ENV = "MEMORAX_CODE_CODEX_HOOK_TRUST_SELECTION_JSON";
|
|
32
8
|
const PREFIX = "[MemoraX Code Install]:";
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const RED = "\x1b[31m";
|
|
38
|
-
const BOLD = "\x1b[1m";
|
|
39
|
-
const UNDERLINE = "\x1b[4m";
|
|
40
|
-
const RESET = "\x1b[0m";
|
|
41
|
-
|
|
42
|
-
if (truthyEnv(process.env.MEMORAX_CODE_SKIP_POSTINSTALL)) process.exit(0);
|
|
43
|
-
const skipCodexPluginInstall = truthyEnv(process.env.MEMORAX_CODE_SKIP_CODEX_PLUGIN_INSTALL);
|
|
44
|
-
const skipClaudeAdapterInstall = truthyEnv(process.env.MEMORAX_CODE_SKIP_CLAUDE_ADAPTER_INSTALL);
|
|
45
|
-
const skipOpenCodeAdapterInstall = truthyEnv(process.env.MEMORAX_CODE_SKIP_OPENCODE_ADAPTER_INSTALL);
|
|
46
|
-
|
|
47
|
-
const scriptDir = dirname(fileURLToPath(import.meta.url));
|
|
48
|
-
const memoraxCodeBin = join(scriptDir, "memorax-code.mjs");
|
|
49
|
-
const memoraxCliBin = join(scriptDir, "memorax-cli.mjs");
|
|
50
|
-
const verbose = process.env.MEMORAX_CODE_NPM_POSTINSTALL_VERBOSE === "1";
|
|
51
|
-
const codexRuntime = ensureCodexCommandEnv();
|
|
52
|
-
const codexCommand = codexRuntime.command;
|
|
53
|
-
const claudeRuntime = ensureClaudeCommandEnv();
|
|
54
|
-
const claudeCommand = claudeRuntime.command;
|
|
55
|
-
const updatePostinstall = postinstallUpdateMode();
|
|
56
|
-
const scriptedAnswers = (canPrompt() || canPromptForUpdate()) && process.stdin.isTTY !== true
|
|
57
|
-
? parseScriptedAnswers(readFileSync(0, "utf8"))
|
|
58
|
-
: undefined;
|
|
59
|
-
const previousClients = readPersistedClientSelection();
|
|
60
|
-
const persistedDshSelection = readPersistedDshSelection();
|
|
61
|
-
let dshProfiles = [];
|
|
62
|
-
let dshProfilesVerified = true;
|
|
63
|
-
try {
|
|
64
|
-
dshProfiles = discoverDshProfiles();
|
|
65
|
-
} catch (error) {
|
|
66
|
-
dshProfilesVerified = false;
|
|
67
|
-
const code = typeof error?.code === "string" ? ` (${error.code})` : "";
|
|
68
|
-
logRed(`DeepSeek Harness Profile discovery could not be verified${code}; DSH setup was skipped, but other client setup will continue.`);
|
|
69
|
-
}
|
|
70
|
-
const dshSelected = dshProfilesVerified
|
|
71
|
-
&& dshProfiles.length > 0
|
|
72
|
-
&& persistedDshSelection !== false;
|
|
73
|
-
if (seedMissingMemoraxCodeConfig() === "failed") {
|
|
74
|
-
printPostinstallSummary("not-verified");
|
|
75
|
-
process.exit(0);
|
|
76
|
-
}
|
|
77
|
-
let stagedHookRuntime;
|
|
78
|
-
try {
|
|
79
|
-
stagedHookRuntime = await stagePackagedClientHookRuntime({
|
|
80
|
-
packageRoot: dirname(scriptDir),
|
|
81
|
-
memoraxCodeHome: memoraxCodeHome(),
|
|
82
|
-
});
|
|
83
|
-
process.env.MEMORAX_CODE_DEFER_CLIENT_HOOK_RUNTIME_ACTIVATION = "1";
|
|
84
|
-
} catch (error) {
|
|
85
|
-
logRed(`Client Hook runtime staging failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
86
|
-
logRed("Backend and plugin mutation were skipped; the previously active runtime remains authoritative.");
|
|
87
|
-
printPostinstallSummary("not-verified");
|
|
88
|
-
process.exit(0);
|
|
89
|
-
}
|
|
90
|
-
runCommonPreflight();
|
|
91
|
-
const requestedClients = ["codex", "claude", "opencode"];
|
|
92
|
-
const codexPreflight = requestedClients.includes("codex") && !skipCodexPluginInstall
|
|
93
|
-
? runCodexPreflight({
|
|
94
|
-
integrationSelected: !updatePostinstall
|
|
95
|
-
|| previousClients === undefined
|
|
96
|
-
|| previousClients.includes("codex"),
|
|
97
|
-
})
|
|
98
|
-
: { ok: true, pluginCache: { marketplaceName: CLI_MARKETPLACE_NAME, versions: [] } };
|
|
99
|
-
const claudePreflight = requestedClients.includes("claude") && !skipClaudeAdapterInstall
|
|
100
|
-
? runClaudePreflight({
|
|
101
|
-
integrationSelected: !updatePostinstall
|
|
102
|
-
|| previousClients === undefined
|
|
103
|
-
|| previousClients.includes("claude"),
|
|
104
|
-
})
|
|
105
|
-
: { ok: true };
|
|
106
|
-
const opencodePreflight = requestedClients.includes("opencode") && !skipOpenCodeAdapterInstall
|
|
107
|
-
? runOpenCodePreflight({
|
|
108
|
-
integrationSelected: !updatePostinstall
|
|
109
|
-
|| previousClients === undefined
|
|
110
|
-
|| previousClients.includes("opencode"),
|
|
111
|
-
})
|
|
112
|
-
: { ok: true };
|
|
113
|
-
const detectedClients = requestedClients.filter((client) => {
|
|
114
|
-
if (client === "codex") return !skipCodexPluginInstall && codexPreflight.ok;
|
|
115
|
-
if (client === "claude") return !skipClaudeAdapterInstall && claudePreflight.ok;
|
|
116
|
-
return !skipOpenCodeAdapterInstall && opencodePreflight.ok;
|
|
117
|
-
});
|
|
118
|
-
const selectedClients = updatePostinstall && previousClients !== undefined
|
|
119
|
-
? await chooseUpdateClients(previousClients, detectedClients, scriptedAnswers)
|
|
120
|
-
: detectedClients;
|
|
121
|
-
const installClients = detectedClients.filter((client) => selectedClients.includes(client));
|
|
122
|
-
if (updatePostinstall && previousClients !== undefined) {
|
|
123
|
-
log(clientSelectionMessage(selectedClients, { dshSelected }));
|
|
124
|
-
} else {
|
|
125
|
-
log(detectedClientMessage(installClients, dshSelected ? dshProfiles : []));
|
|
126
|
-
}
|
|
127
|
-
if (dshSelected) {
|
|
128
|
-
log(`DeepSeek Harness profiles: found (${dshProfiles.map((profile) => profile.name).join(", ")}); supported profiles will be reconciled by \`memorax-code start\`.`);
|
|
129
|
-
} else if (dshProfiles.length > 0) {
|
|
130
|
-
log("DeepSeek Harness profiles were detected, but the DSH integration is disabled by [clients].dsh.");
|
|
131
|
-
}
|
|
132
|
-
if (requestedClients.includes("codex") && !skipCodexPluginInstall && !codexPreflight.ok) {
|
|
133
|
-
log("Codex runtime was not detected; skipping its adapter setup.");
|
|
134
|
-
}
|
|
135
|
-
if (requestedClients.includes("claude") && !skipClaudeAdapterInstall && !claudePreflight.ok) {
|
|
136
|
-
log("Claude Code runtime was not detected; skipping its adapter setup.");
|
|
137
|
-
}
|
|
138
|
-
if (requestedClients.includes("opencode") && !skipOpenCodeAdapterInstall && !opencodePreflight.ok) {
|
|
139
|
-
log("OpenCode runtime or configuration was not detected; skipping its adapter setup.");
|
|
140
|
-
}
|
|
141
|
-
if (writeClientSelectionConfig(selectedClients) === "failed") {
|
|
142
|
-
printPostinstallSummary("not-verified");
|
|
143
|
-
process.exit(0);
|
|
144
|
-
}
|
|
145
|
-
const clientMode = clientModeFor(installClients, {
|
|
146
|
-
includeDsh: dshProfilesVerified && persistedDshSelection !== false,
|
|
147
|
-
});
|
|
148
|
-
let memoraxConfigResult = "skipped";
|
|
149
|
-
if (installClients.length > 0 || dshSelected) {
|
|
150
|
-
memoraxConfigResult = await maybeConfigureMemoraxMemory(scriptedAnswers);
|
|
151
|
-
}
|
|
152
|
-
if (memoraxConfigResult === "configured") {
|
|
153
|
-
if (seedMissingMemoraxCodeConfig() === "failed") {
|
|
154
|
-
printPostinstallSummary("not-verified");
|
|
155
|
-
process.exit(0);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
const codexClientEnabled = installClients.includes("codex");
|
|
159
|
-
const claudeClientEnabled = installClients.includes("claude");
|
|
160
|
-
const opencodeClientEnabled = installClients.includes("opencode");
|
|
161
|
-
const codexClientNewlyEnabled = codexClientEnabled
|
|
162
|
-
&& updatePostinstall
|
|
163
|
-
&& previousClients !== undefined
|
|
164
|
-
&& !previousClients.includes("codex");
|
|
165
|
-
const codexHooksBeforeUpdate = codexClientEnabled && updatePostinstall && !codexClientNewlyEnabled
|
|
166
|
-
? inspectCodexPluginHooksForUpdate()
|
|
167
|
-
: undefined;
|
|
168
|
-
const result = codexClientEnabled
|
|
169
|
-
? runNodeMemoraxCodeCommand(["codex-plugin", "install", "--json"], { print: verbose })
|
|
170
|
-
: { status: 0 };
|
|
171
|
-
|
|
172
|
-
if (codexClientEnabled && result.status !== 0 && process.env.npm_lifecycle_event) {
|
|
173
|
-
logRed("MemoraX Code Codex plugin registration was skipped or failed. Run `memorax-code codex-plugin install` after installation.");
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
if (codexClientEnabled && result.status === 0) {
|
|
177
|
-
if (codexClientNewlyEnabled) {
|
|
178
|
-
await maybeActivateCodexPluginHooks(scriptedAnswers, { updatePrompt: true });
|
|
179
|
-
} else if (updatePostinstall) {
|
|
180
|
-
await maybeTrustUpdatedCodexPluginHooks(scriptedAnswers, codexHooksBeforeUpdate);
|
|
181
|
-
} else {
|
|
182
|
-
await maybeActivateCodexPluginHooks(scriptedAnswers);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
const skipCodexAdapter = !codexClientEnabled;
|
|
186
|
-
const skipClaudeAdapter = !claudeClientEnabled;
|
|
187
|
-
const skipOpenCodeAdapter = !opencodeClientEnabled;
|
|
188
|
-
const codexSkipReason = postinstallClientSkipReason({
|
|
189
|
-
explicitlySkipped: skipCodexPluginInstall,
|
|
190
|
-
selected: selectedClients.includes("codex"),
|
|
191
|
-
enabled: codexClientEnabled,
|
|
192
|
-
});
|
|
193
|
-
const claudeSkipReason = postinstallClientSkipReason({
|
|
194
|
-
explicitlySkipped: skipClaudeAdapterInstall,
|
|
195
|
-
selected: selectedClients.includes("claude"),
|
|
196
|
-
enabled: claudeClientEnabled,
|
|
197
|
-
});
|
|
198
|
-
const opencodeSkipReason = postinstallClientSkipReason({
|
|
199
|
-
explicitlySkipped: skipOpenCodeAdapterInstall,
|
|
200
|
-
selected: selectedClients.includes("opencode"),
|
|
201
|
-
enabled: opencodeClientEnabled,
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
const backendAndAdapters = startBackendAndCheck({
|
|
205
|
-
skipCodexAdapter,
|
|
206
|
-
clientMode,
|
|
207
|
-
codexSkipReason,
|
|
208
|
-
skipClaudeAdapter,
|
|
209
|
-
claudeAdapterRequired: !skipClaudeAdapter,
|
|
210
|
-
claudeSkipReason,
|
|
211
|
-
skipOpenCodeAdapter,
|
|
212
|
-
opencodeAdapterRequired: !skipOpenCodeAdapter,
|
|
213
|
-
opencodeSkipReason,
|
|
214
|
-
});
|
|
215
|
-
const backendAndAdaptersStatus = backendAndAdapters.status;
|
|
216
|
-
if (backendAndAdaptersStatus === "enabled") {
|
|
217
|
-
logGreen(`Client Hook runtime ${stagedHookRuntime.generationId} activated.`);
|
|
218
|
-
}
|
|
219
|
-
if (backendAndAdaptersStatus === "enabled") {
|
|
220
|
-
printNextSteps({
|
|
221
|
-
codexAdapterEnabled: !skipCodexAdapter,
|
|
222
|
-
claudeAdapterEnabled: !skipClaudeAdapter,
|
|
223
|
-
dshAdapterEnabled: backendAndAdapters.dshAdapterEnabled,
|
|
224
|
-
opencodeAdapterEnabled: !skipOpenCodeAdapter,
|
|
225
|
-
});
|
|
226
|
-
printCommonCommands({
|
|
227
|
-
codexAdapterEnabled: !skipCodexAdapter,
|
|
228
|
-
claudeAdapterEnabled: !skipClaudeAdapter,
|
|
229
|
-
opencodeAdapterEnabled: !skipOpenCodeAdapter,
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
printPostinstallSummary(backendAndAdaptersStatus);
|
|
233
|
-
if (backendAndAdaptersStatus === "enabled") {
|
|
234
|
-
log("View local memory activity: http://127.0.0.1:8787/memory-viewer");
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// Do not fail npm installation: users can register the plugin explicitly later.
|
|
238
|
-
process.exit(0);
|
|
239
|
-
|
|
240
|
-
async function maybeConfigureMemoraxMemory(scriptedAnswers) {
|
|
241
|
-
printMemoraxDisclosure();
|
|
242
|
-
const promptAvailable = canPrompt();
|
|
243
|
-
if (promptAvailable && readMemoraxInstallStatus()?.configured) {
|
|
244
|
-
log("Existing MemoraX credentials detected; keeping the effective configuration unchanged.");
|
|
245
|
-
return "preserved";
|
|
246
|
-
}
|
|
247
|
-
if (!promptAvailable) {
|
|
248
|
-
log(`This install cannot prompt for a MemoraX ID and key. Register at ${MEMORAX_ACCOUNT_URL}, then edit \`~/.memorax-code/config.toml\` or rerun interactively with \`--foreground-scripts\`.`);
|
|
249
|
-
return "skipped";
|
|
250
|
-
}
|
|
251
|
-
if (scriptedAnswers) {
|
|
252
|
-
return await configureMemoraxMemoryFromAnswers(scriptedAnswers);
|
|
253
|
-
}
|
|
254
|
-
printMemoraxRegistrationLink();
|
|
255
|
-
printMemoraxUserIdGuidance();
|
|
256
|
-
let rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
257
|
-
try {
|
|
258
|
-
const userId = await questionRequiredLine(
|
|
259
|
-
rl,
|
|
260
|
-
`${PREFIX} Your MemoraX user ID (required, e.g. your-name): `,
|
|
261
|
-
"MemoraX user ID is required.",
|
|
262
|
-
);
|
|
263
|
-
const outputLanguage = await questionPreferredLanguage(rl);
|
|
264
|
-
rl.close();
|
|
265
|
-
rl = undefined;
|
|
266
|
-
const apiKey = await questionRequiredMasked(`${PREFIX} MemoraX API key (required): `);
|
|
267
|
-
return await writeMemoraxConfigFromInput({
|
|
268
|
-
userId,
|
|
269
|
-
apiKey,
|
|
270
|
-
endpoint: memoraxInstallEndpoint(),
|
|
271
|
-
outputLanguage,
|
|
272
|
-
});
|
|
273
|
-
} finally {
|
|
274
|
-
rl?.close();
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
async function chooseUpdateClients(previousClients, detectedClients, scriptedAnswers) {
|
|
279
|
-
const selected = new Set(previousClients);
|
|
280
|
-
const availableDisabledClients = detectedClients.filter((client) => !selected.has(client));
|
|
281
|
-
if (availableDisabledClients.length === 0) return [...previousClients];
|
|
282
|
-
|
|
283
|
-
if (!canPromptForUpdate()) {
|
|
284
|
-
for (const client of availableDisabledClients) {
|
|
285
|
-
const label = clientLabel(client);
|
|
286
|
-
log(`${label} runtime is available, but its integration remains disabled because this update cannot prompt. Rerun \`memorax-code update\` from an interactive terminal to choose whether to enable it.`);
|
|
287
|
-
}
|
|
288
|
-
return [...previousClients];
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
let rl;
|
|
292
|
-
try {
|
|
293
|
-
for (const client of availableDisabledClients) {
|
|
294
|
-
const label = clientLabel(client);
|
|
295
|
-
const question = `${label} runtime is available, but its integration is disabled in [clients]. Enable it now? [Y/n]`;
|
|
296
|
-
let answer;
|
|
297
|
-
if (scriptedAnswers) {
|
|
298
|
-
log(question);
|
|
299
|
-
answer = String(scriptedAnswers.shift() ?? "").trim();
|
|
300
|
-
} else {
|
|
301
|
-
rl ??= createInterface({ input: process.stdin, output: process.stderr });
|
|
302
|
-
answer = (await rl.question(`${PREFIX} ${question} `)).trim();
|
|
303
|
-
}
|
|
304
|
-
if (/^n(?:o)?$/i.test(answer)) {
|
|
305
|
-
log(`Keeping the ${label} integration disabled.`);
|
|
306
|
-
} else {
|
|
307
|
-
selected.add(client);
|
|
308
|
-
logGreen(`Enabling the ${label} integration.`);
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
} finally {
|
|
312
|
-
rl?.close();
|
|
313
|
-
}
|
|
314
|
-
return ["codex", "claude", "opencode"].filter((client) => selected.has(client));
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
async function configureMemoraxMemoryFromAnswers(answers) {
|
|
318
|
-
printMemoraxRegistrationLink();
|
|
319
|
-
printMemoraxUserIdGuidance();
|
|
320
|
-
const userId = requiredScriptedAnswer(answers, {
|
|
321
|
-
prompt: "Your MemoraX user ID (required, e.g. your-name)",
|
|
322
|
-
requiredMessage: "MemoraX user ID is required.",
|
|
323
|
-
missingMessage: "No MemoraX user ID response was received; MemoraX configuration is incomplete.",
|
|
324
|
-
});
|
|
325
|
-
if (!userId) return "skipped";
|
|
326
|
-
const outputLanguage = scriptedPreferredLanguage(answers);
|
|
327
|
-
if (!outputLanguage) return "skipped";
|
|
328
|
-
const apiKey = requiredScriptedAnswer(answers, {
|
|
329
|
-
prompt: "MemoraX API key (required)",
|
|
330
|
-
requiredMessage: "MemoraX API key is required.",
|
|
331
|
-
missingMessage: "No MemoraX API key response was received; MemoraX configuration is incomplete.",
|
|
332
|
-
});
|
|
333
|
-
if (!apiKey) return "skipped";
|
|
334
|
-
return await writeMemoraxConfigFromInput({
|
|
335
|
-
userId,
|
|
336
|
-
apiKey,
|
|
337
|
-
endpoint: memoraxInstallEndpoint(),
|
|
338
|
-
outputLanguage,
|
|
339
|
-
});
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
function printMemoraxDisclosure() {
|
|
343
|
-
log("MemoraX Code requires MemoraX for its core remote-memory functionality.");
|
|
344
|
-
log("After connection, trusted repository sessions automatically send selected user prompts and final assistant answers to MemoraX after replies.");
|
|
345
|
-
log("Newly generated configuration enables automatic writeback. Existing configuration is never enabled implicitly; disable it with `[memory.writeback] enabled = false` or `MEMORAX_CODE_MEMORAX_WRITEBACK_ENABLED=false`.");
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
function printMemoraxUserIdGuidance() {
|
|
349
|
-
log("MemoraX uses one user ID to keep your coding memories consistent across supported agents and devices.");
|
|
350
|
-
log("Choose a stable ID and use the same value everywhere; MemoraX Code separates repositories automatically.");
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
function printMemoraxRegistrationLink() {
|
|
354
|
-
log("No MemoraX account or API key yet?");
|
|
355
|
-
log(`→ Register here: ${BRIGHT_CYAN}${BOLD}${UNDERLINE}${MEMORAX_ACCOUNT_URL}${RESET}`);
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
async function writeMemoraxConfigFromInput({ userId, apiKey, endpoint, outputLanguage }) {
|
|
359
|
-
if (!userId || !apiKey) {
|
|
360
|
-
logRed("MemoraX config was not written because MemoraX user ID or API key was empty.");
|
|
361
|
-
return "skipped";
|
|
362
|
-
}
|
|
363
|
-
if (!outputLanguage) {
|
|
364
|
-
logRed("MemoraX config was not written because preferred language must be zh or en.");
|
|
365
|
-
return "skipped";
|
|
366
|
-
}
|
|
367
|
-
if (writeMemoraxConfig({ userId, apiKey, endpoint, outputLanguage }) === "failed") {
|
|
368
|
-
logRed("MemoraX config was not written because the existing config could not be safely updated.");
|
|
369
|
-
return "skipped";
|
|
370
|
-
}
|
|
371
|
-
logGreen(`MemoraX config written to ${memoraxCodeConfigPath()}.`);
|
|
372
|
-
log("MemoraX network access will be checked by the first workspace-scoped memory request from a trusted workspace.");
|
|
373
|
-
return "configured";
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
async function questionRequiredLine(rl, prompt, requiredMessage) {
|
|
377
|
-
while (true) {
|
|
378
|
-
const answer = (await rl.question(prompt)).trim();
|
|
379
|
-
if (answer) return answer;
|
|
380
|
-
logRed(requiredMessage);
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
async function questionRequiredMasked(prompt) {
|
|
385
|
-
while (true) {
|
|
386
|
-
const answer = (await questionMasked(prompt)).trim();
|
|
387
|
-
if (answer) return answer;
|
|
388
|
-
logRed("MemoraX API key is required.");
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
function requiredScriptedAnswer(answers, { prompt, requiredMessage, missingMessage }) {
|
|
393
|
-
while (answers.length > 0) {
|
|
394
|
-
const answer = String(answers.shift() ?? "").trim();
|
|
395
|
-
log(`${prompt}: ${answer ? "<provided>" : "<empty>"}`);
|
|
396
|
-
if (answer) return answer;
|
|
397
|
-
logRed(requiredMessage);
|
|
398
|
-
}
|
|
399
|
-
logRed(missingMessage);
|
|
400
|
-
return undefined;
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
function scriptedPreferredLanguage(answers) {
|
|
404
|
-
while (answers.length > 0) {
|
|
405
|
-
const answer = String(answers.shift() ?? "").trim();
|
|
406
|
-
log(`Preferred language [ZH/en] (used for Memory extraction): ${answer ? "<provided>" : "<default>"}`);
|
|
407
|
-
const language = preferredLanguage(answer);
|
|
408
|
-
if (language) return language;
|
|
409
|
-
logRed("Preferred language must be zh or en.");
|
|
410
|
-
}
|
|
411
|
-
logRed("No preferred language response was received; MemoraX configuration is incomplete.");
|
|
412
|
-
return undefined;
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
async function questionPreferredLanguage(rl) {
|
|
416
|
-
while (true) {
|
|
417
|
-
const answer = await rl.question(`${PREFIX} Preferred language [ZH/en] (used for Memory extraction): `);
|
|
418
|
-
const language = preferredLanguage(answer);
|
|
419
|
-
if (language) return language;
|
|
420
|
-
logRed("Preferred language must be zh or en.");
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
function preferredLanguage(value) {
|
|
425
|
-
const trimmed = String(value ?? "").trim();
|
|
426
|
-
return trimmed
|
|
427
|
-
? normalizeMemoraxMemoryOutputLanguage(trimmed)
|
|
428
|
-
: MEMORAX_DEFAULT_MEMORY_OUTPUT_LANGUAGE;
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
function memoraxInstallEndpoint() {
|
|
432
|
-
return normalizeMemoraxBaseUrl(process.env.MEMORAX_CODE_MEMORAX_ENDPOINT) || MEMORAX_DEFAULT_BASE_URL;
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
function isRecord(value) {
|
|
436
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9
|
+
const nodeVersionError = unsupportedNodeVersionMessage();
|
|
10
|
+
if (nodeVersionError) {
|
|
11
|
+
console.error(`${PREFIX} ${nodeVersionError}`);
|
|
12
|
+
process.exit(1);
|
|
437
13
|
}
|
|
438
14
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
442
|
-
try {
|
|
443
|
-
return await rl.question(prompt);
|
|
444
|
-
} finally {
|
|
445
|
-
rl.close();
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
process.stderr.write(prompt);
|
|
449
|
-
const wasRaw = process.stdin.isRaw === true;
|
|
450
|
-
process.stdin.setRawMode(true);
|
|
451
|
-
process.stdin.resume();
|
|
452
|
-
return await new Promise((resolve, reject) => {
|
|
453
|
-
let answer = "";
|
|
454
|
-
const cleanup = () => {
|
|
455
|
-
process.stdin.off("data", onData);
|
|
456
|
-
process.stdin.setRawMode(wasRaw);
|
|
457
|
-
if (!wasRaw) process.stdin.pause();
|
|
458
|
-
};
|
|
459
|
-
const finish = () => {
|
|
460
|
-
cleanup();
|
|
461
|
-
process.stderr.write("\n");
|
|
462
|
-
resolve(answer);
|
|
463
|
-
};
|
|
464
|
-
const onData = (chunk) => {
|
|
465
|
-
for (const char of String(chunk)) {
|
|
466
|
-
if (char === "\u0003") {
|
|
467
|
-
cleanup();
|
|
468
|
-
process.stderr.write("\n");
|
|
469
|
-
reject(new Error("Interrupted"));
|
|
470
|
-
return;
|
|
471
|
-
}
|
|
472
|
-
if (char === "\r" || char === "\n") {
|
|
473
|
-
finish();
|
|
474
|
-
return;
|
|
475
|
-
}
|
|
476
|
-
if (char === "\u0008" || char === "\u007f") {
|
|
477
|
-
if (answer.length > 0) {
|
|
478
|
-
answer = answer.slice(0, -1);
|
|
479
|
-
process.stderr.write("\b \b");
|
|
480
|
-
}
|
|
481
|
-
continue;
|
|
482
|
-
}
|
|
483
|
-
if (char >= " " && char !== "\u007f") {
|
|
484
|
-
answer += char;
|
|
485
|
-
process.stderr.write("*");
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
};
|
|
489
|
-
process.stdin.on("data", onData);
|
|
490
|
-
});
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
function inspectCodexPluginHooksForUpdate() {
|
|
494
|
-
const result = runNodeMemoraxCodeCommand(codexPluginHookArgs("hooks"), { print: false });
|
|
495
|
-
const report = parseCodexPluginHookReport(result, "codex-plugin-hooks");
|
|
496
|
-
return report?.hooks;
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
async function maybeTrustUpdatedCodexPluginHooks(scriptedAnswers, previousHooks) {
|
|
500
|
-
if (!previousHooks) {
|
|
501
|
-
warnUpdatedHookTrustSkipped("Existing Codex hooks could not be inspected before the plugin cache refresh.");
|
|
502
|
-
return "skipped";
|
|
503
|
-
}
|
|
504
|
-
const checkEnv = hookTrustCommandEnv(PREVIOUS_HOOKS_ENV, previousHooks);
|
|
505
|
-
const checked = runNodeMemoraxCodeCommand(codexPluginHookArgs("trust-hooks", ["--check"]), {
|
|
506
|
-
env: checkEnv,
|
|
507
|
-
print: false,
|
|
508
|
-
});
|
|
509
|
-
const report = parseCodexPluginHookReport(checked, "codex-plugin-trust-hooks");
|
|
510
|
-
if (!report) {
|
|
511
|
-
warnUpdatedHookTrustSkipped("Updated Codex hooks could not be inspected after the plugin cache refresh.");
|
|
512
|
-
return "skipped";
|
|
513
|
-
}
|
|
514
|
-
if (report.requiresFullReview) {
|
|
515
|
-
warnUpdatedHookTrustSkipped("The MemoraX Code Codex plugin marketplace identity changed during the update, so incremental Hook authorization was not applied.");
|
|
516
|
-
return "skipped";
|
|
517
|
-
}
|
|
518
|
-
if (report.hooks.length === 0) return "unchanged";
|
|
519
|
-
|
|
520
|
-
printUpdatedCodexHooks(report.hooks, previousHooks);
|
|
521
|
-
if (!canPromptForUpdate()) {
|
|
522
|
-
warnUpdatedHookTrustSkipped("This update is running without an interactive terminal, so the new or changed Hooks remain untrusted.");
|
|
523
|
-
return "skipped";
|
|
524
|
-
}
|
|
525
|
-
const answer = await updatedHookTrustAnswer(scriptedAnswers);
|
|
526
|
-
if (answer === undefined) {
|
|
527
|
-
warnUpdatedHookTrustSkipped("No Hook authorization response was received, so the new or changed Hooks remain untrusted.");
|
|
528
|
-
return "skipped";
|
|
529
|
-
}
|
|
530
|
-
const normalizedAnswer = answer.trim();
|
|
531
|
-
if (normalizedAnswer && !/^y(?:es)?$/i.test(normalizedAnswer)) {
|
|
532
|
-
warnUpdatedHookTrustSkipped("Hook authorization was declined, so the new or changed Hooks remain untrusted.");
|
|
533
|
-
return "skipped";
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
const trustEnv = hookTrustCommandEnv(HOOK_TRUST_SELECTION_ENV, report.hooks);
|
|
537
|
-
const trusted = runNodeMemoraxCodeCommand(codexPluginHookArgs("trust-hooks", ["--yes"]), {
|
|
538
|
-
env: trustEnv,
|
|
539
|
-
print: verbose,
|
|
540
|
-
});
|
|
541
|
-
if (trusted.status === 0) {
|
|
542
|
-
logGreen(`Trusted ${report.hooks.length} new or changed MemoraX Code Codex Hook${report.hooks.length === 1 ? "" : "s"}.`);
|
|
543
|
-
return "trusted";
|
|
544
|
-
}
|
|
545
|
-
warnUpdatedHookTrustSkipped("The reviewed Hooks changed again or could not be written to Codex config.");
|
|
546
|
-
return "failed";
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
function parseCodexPluginHookReport(result, action) {
|
|
550
|
-
if (result.status !== 0) return undefined;
|
|
551
|
-
try {
|
|
552
|
-
const report = JSON.parse(String(result.stdout ?? ""));
|
|
553
|
-
if (!report || report.ok !== true || report.action !== action || !Array.isArray(report.hooks)) return undefined;
|
|
554
|
-
if (!report.hooks.every((hook) => hook
|
|
555
|
-
&& typeof hook === "object"
|
|
556
|
-
&& typeof hook.key === "string"
|
|
557
|
-
&& typeof hook.currentHash === "string"
|
|
558
|
-
&& hook.handlerType === "command"
|
|
559
|
-
&& typeof hook.eventName === "string"
|
|
560
|
-
&& hook.eventName.length > 0
|
|
561
|
-
&& typeof hook.command === "string"
|
|
562
|
-
&& hook.command.length > 0
|
|
563
|
-
&& isCodexHookTrustStatus(hook.trustStatus))) return undefined;
|
|
564
|
-
if (action === "codex-plugin-trust-hooks"
|
|
565
|
-
&& (typeof report.requiresFullReview !== "boolean"
|
|
566
|
-
|| !report.hooks.every((hook) => hook.trustStatus === "untrusted" || hook.trustStatus === "modified"))) {
|
|
567
|
-
return undefined;
|
|
568
|
-
}
|
|
569
|
-
return report;
|
|
570
|
-
} catch {
|
|
571
|
-
return undefined;
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
function isCodexHookTrustStatus(value) {
|
|
576
|
-
return value === "managed" || value === "untrusted" || value === "trusted" || value === "modified";
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
function codexPluginHookArgs(subcommand, extraArgs = []) {
|
|
580
|
-
const args = ["codex-plugin", subcommand, ...extraArgs];
|
|
581
|
-
const codexCommand = stringOption(process.env.MEMORAX_CODE_CODEX_COMMAND);
|
|
582
|
-
if (codexCommand) args.push("--codex-command", codexCommand);
|
|
583
|
-
const workspace = codexHookInspectionWorkspace();
|
|
584
|
-
if (workspace) args.push("--workspace", workspace);
|
|
585
|
-
args.push("--json");
|
|
586
|
-
return args;
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
function codexHookInspectionWorkspace() {
|
|
590
|
-
for (const candidate of [process.env.INIT_CWD, process.env.HOME, homedir(), process.cwd()]) {
|
|
591
|
-
if (candidate && existsSync(candidate)) return candidate;
|
|
592
|
-
}
|
|
593
|
-
return undefined;
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
function hookTrustCommandEnv(name, hooks) {
|
|
597
|
-
const env = { ...process.env };
|
|
598
|
-
delete env[PREVIOUS_HOOKS_ENV];
|
|
599
|
-
delete env[HOOK_TRUST_SELECTION_ENV];
|
|
600
|
-
env[name] = JSON.stringify(hooks);
|
|
601
|
-
return env;
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
function printUpdatedCodexHooks(hooks, previousHooks) {
|
|
605
|
-
const previousByKey = new Map(previousHooks.map((hook) => [hook.key, hook.currentHash]));
|
|
606
|
-
log("This MemoraX Code update includes new or changed Codex Hooks that require authorization:");
|
|
607
|
-
for (const hook of hooks) {
|
|
608
|
-
const change = previousByKey.has(hook.key) ? "changed" : "new";
|
|
609
|
-
const label = hook.statusMessage ? ` (${hook.statusMessage})` : "";
|
|
610
|
-
log(`- ${change}: ${hook.key}${label}`);
|
|
611
|
-
if (hook.eventName) log(` event: ${hook.eventName}`);
|
|
612
|
-
if (hook.command) log(` command: ${hook.command}`);
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
async function updatedHookTrustAnswer(scriptedAnswers) {
|
|
617
|
-
if (scriptedAnswers) {
|
|
618
|
-
log("Trust these new or changed Codex Hooks? [Y/n]");
|
|
619
|
-
return scriptedAnswers.shift();
|
|
620
|
-
}
|
|
621
|
-
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
622
|
-
try {
|
|
623
|
-
return await rl.question(`${PREFIX} Trust these new or changed Codex Hooks? [Y/n] `);
|
|
624
|
-
} finally {
|
|
625
|
-
rl.close();
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
function parseScriptedAnswers(input) {
|
|
630
|
-
if (input.length === 0) return [];
|
|
631
|
-
const answers = input.split(/\r?\n/);
|
|
632
|
-
if (answers.at(-1) === "") answers.pop();
|
|
633
|
-
return answers;
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
function warnUpdatedHookTrustSkipped(message) {
|
|
637
|
-
logRed(message);
|
|
638
|
-
logRed("Review and authorize the current MemoraX Code Codex Hooks with `memorax-code codex-plugin trust-hooks`.");
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
async function maybeActivateCodexPluginHooks(scriptedAnswers, { updatePrompt = false } = {}) {
|
|
642
|
-
if (!(updatePrompt ? canPromptForUpdate() : canPrompt())) {
|
|
643
|
-
log("Codex hook activation was not prompted. Run `memorax-code codex-plugin activate --yes` later to activate and trust MemoraX Code Codex Adapter hooks.");
|
|
644
|
-
return "skipped";
|
|
645
|
-
}
|
|
646
|
-
if (scriptedAnswers) {
|
|
647
|
-
return activateCodexPluginHooksFromAnswers(scriptedAnswers);
|
|
648
|
-
}
|
|
649
|
-
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
650
|
-
try {
|
|
651
|
-
const activateNow = await rl.question(`${PREFIX} Activate and trust MemoraX Code Codex Adapter hooks now? [Y/n] `);
|
|
652
|
-
return activateCodexPluginHooksFromAnswer(activateNow);
|
|
653
|
-
} finally {
|
|
654
|
-
rl.close();
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
function activateCodexPluginHooksFromAnswers(answers) {
|
|
659
|
-
log("Activate and trust MemoraX Code Codex Adapter hooks now? [Y/n]");
|
|
660
|
-
const activateNow = String(answers.shift() ?? "").trim();
|
|
661
|
-
return activateCodexPluginHooksFromAnswer(activateNow);
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
function activateCodexPluginHooksFromAnswer(answer) {
|
|
665
|
-
if (/^n(?:o)?$/i.test(String(answer ?? "").trim())) {
|
|
666
|
-
log("Codex hook activation skipped; run `memorax-code codex-plugin activate --yes` later if needed.");
|
|
667
|
-
return "skipped";
|
|
668
|
-
}
|
|
669
|
-
const activated = runNodeMemoraxCodeCommand(["codex-plugin", "activate", "--yes"], { print: verbose });
|
|
670
|
-
if (activated.status === 0) {
|
|
671
|
-
logGreen("MemoraX Code Codex Adapter hooks activated and trusted.");
|
|
672
|
-
return "activated";
|
|
673
|
-
}
|
|
674
|
-
logRed("Codex hook activation failed; run `memorax-code codex-plugin activate --yes` after installation.");
|
|
675
|
-
return "failed";
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
function canPrompt() {
|
|
679
|
-
if (updatePostinstall) return false;
|
|
680
|
-
return canPromptOnStderr();
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
function canPromptForUpdate() {
|
|
684
|
-
return updatePostinstall && canPromptOnStderr();
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
function canPromptOnStderr() {
|
|
688
|
-
return process.env.MEMORAX_CODE_NPM_POSTINSTALL_ASSUME_INTERACTIVE === "1"
|
|
689
|
-
|| (process.stdin.isTTY === true && process.stderr.isTTY === true);
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
function postinstallUpdateMode() {
|
|
693
|
-
return truthyEnv(process.env.MEMORAX_CODE_NPM_POSTINSTALL_UPDATE) || process.env.npm_command === "update";
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
function truthyEnv(value) {
|
|
697
|
-
return ["1", "true", "yes"].includes(String(value ?? "").toLowerCase());
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
function memoraxCodeHome() {
|
|
701
|
-
return process.env.MEMORAX_CODE_HOME || join(homedir(), ".memorax-code");
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
function memoraxCodeConfigPath() {
|
|
705
|
-
return join(memoraxCodeHome(), "config.toml");
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
function readPersistedClientSelection() {
|
|
709
|
-
const path = memoraxCodeConfigPath();
|
|
710
|
-
if (!existsSync(path)) return undefined;
|
|
711
|
-
try {
|
|
712
|
-
const clients = parse(readFileSync(path, "utf8"))?.clients;
|
|
713
|
-
if (!clients || typeof clients !== "object" || typeof clients.codex !== "boolean" || typeof clients.claude !== "boolean") return undefined;
|
|
714
|
-
return [
|
|
715
|
-
clients.codex ? "codex" : undefined,
|
|
716
|
-
clients.claude ? "claude" : undefined,
|
|
717
|
-
clients.opencode === true ? "opencode" : undefined,
|
|
718
|
-
].filter(Boolean);
|
|
719
|
-
} catch {
|
|
720
|
-
return undefined;
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
function readPersistedDshSelection() {
|
|
725
|
-
const path = memoraxCodeConfigPath();
|
|
726
|
-
if (!existsSync(path)) return undefined;
|
|
727
|
-
try {
|
|
728
|
-
const value = parse(readFileSync(path, "utf8"))?.clients?.dsh;
|
|
729
|
-
return typeof value === "boolean" ? value : undefined;
|
|
730
|
-
} catch {
|
|
731
|
-
return undefined;
|
|
732
|
-
}
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
function writeClientSelectionConfig(clients) {
|
|
736
|
-
const path = memoraxCodeConfigPath();
|
|
737
|
-
return updateConfigFileAtomically({
|
|
738
|
-
path,
|
|
739
|
-
defaultText: setManagedClientSelection(defaultMemoraxCodeConfig(), clients),
|
|
740
|
-
transform: (text) => setManagedClientSelection(text, clients),
|
|
741
|
-
parseToml: parse,
|
|
742
|
-
warn: (message) => log(message),
|
|
743
|
-
});
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
function setManagedClientSelection(text, clients) {
|
|
747
|
-
const withOpenCode = setTomlField(text, "clients", "opencode", String(clients.includes("opencode")));
|
|
748
|
-
const withClaude = setTomlField(withOpenCode, "clients", "claude", String(clients.includes("claude")));
|
|
749
|
-
return setTomlField(withClaude, "clients", "codex", String(clients.includes("codex")));
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
function writeMemoraxConfig({ userId, apiKey, endpoint, outputLanguage }) {
|
|
753
|
-
const path = memoraxCodeConfigPath();
|
|
754
|
-
const fields = [
|
|
755
|
-
{
|
|
756
|
-
key: "endpoint",
|
|
757
|
-
line: `endpoint = "${tomlString(endpoint || MEMORAX_DEFAULT_BASE_URL)}" # MemoraX service URL.`,
|
|
758
|
-
},
|
|
759
|
-
{
|
|
760
|
-
key: "api_key",
|
|
761
|
-
line: `api_key = "${tomlString(apiKey)}" # MemoraX API key used by the local Backend.`,
|
|
762
|
-
},
|
|
763
|
-
{
|
|
764
|
-
key: "user_id",
|
|
765
|
-
line: `user_id = "${tomlString(userId)}" # MemoraX base user ID; requests derive a workspace-scoped namespace.`,
|
|
766
|
-
},
|
|
767
|
-
];
|
|
768
|
-
const addFields = [{
|
|
769
|
-
key: "output_language",
|
|
770
|
-
line: `output_language = "${outputLanguage}" # Language for newly generated MemoraX memories.`,
|
|
771
|
-
}];
|
|
772
|
-
const applyFields = (text) => setTomlSectionFields(
|
|
773
|
-
setTomlSectionFields(text, "memorax", fields),
|
|
774
|
-
"memory.add",
|
|
775
|
-
addFields,
|
|
776
|
-
);
|
|
777
|
-
return updateConfigFileAtomically({
|
|
778
|
-
path,
|
|
779
|
-
defaultText: applyFields(defaultMemoraxCodeConfig()),
|
|
780
|
-
transform: applyFields,
|
|
781
|
-
parseToml: parse,
|
|
782
|
-
warn: (message) => log(message),
|
|
783
|
-
});
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
function defaultMemoraxCodeConfig() {
|
|
787
|
-
return [
|
|
788
|
-
"# MemoraX Code local config.",
|
|
789
|
-
"# This file is read from $MEMORAX_CODE_HOME/config.toml.",
|
|
790
|
-
"# Environment variables still override values written here.",
|
|
791
|
-
"# See docs/configuration.md for advanced tuning fields and effective defaults.",
|
|
792
|
-
"",
|
|
793
|
-
"[clients]",
|
|
794
|
-
"codex = true # Manage the Codex adapter.",
|
|
795
|
-
"claude = true # Manage the Claude adapter.",
|
|
796
|
-
"dsh = true # Manage the DeepSeek Harness adapter when Profiles exist.",
|
|
797
|
-
"opencode = true # Manage the OpenCode adapter.",
|
|
798
|
-
"",
|
|
799
|
-
"# MemoraX remote-memory connection. Credentials may also come from the environment.",
|
|
800
|
-
"[memorax]",
|
|
801
|
-
`# endpoint = "${MEMORAX_DEFAULT_BASE_URL}" # MemoraX service URL.`,
|
|
802
|
-
'# api_key = "" # MemoraX API key used by the local Backend.',
|
|
803
|
-
'# user_id = "" # MemoraX base user ID; requests derive a workspace-scoped namespace.',
|
|
804
|
-
"",
|
|
805
|
-
"# Automatic Hook retrieval is opt-in.",
|
|
806
|
-
"[memory.retrieval]",
|
|
807
|
-
"enabled = false # Auto-inject retrieved memories into supported client prompts.",
|
|
808
|
-
"",
|
|
809
|
-
"# Automatic writeback sends selected prompts and final answers to MemoraX.",
|
|
810
|
-
"[memory.writeback]",
|
|
811
|
-
"enabled = true # Allow supported client sessions to write memories after replies.",
|
|
812
|
-
"",
|
|
813
|
-
"# Preferred language for newly generated MemoraX memories.",
|
|
814
|
-
"[memory.add]",
|
|
815
|
-
`output_language = "${MEMORAX_DEFAULT_MEMORY_OUTPUT_LANGUAGE}" # Language for newly generated MemoraX memories.`,
|
|
816
|
-
"",
|
|
817
|
-
"# Controls how often supported native client sessions see the MemoraX Code skill reminder.",
|
|
818
|
-
"[memory.skill_reminder]",
|
|
819
|
-
"interval_turns = 5 # Show the MemoraX Code skill reminder every N native client turns, starting on the first turn.",
|
|
820
|
-
"",
|
|
821
|
-
"# Relevant repo reads batch generated repo-memory updates by commit count or elapsed time.",
|
|
822
|
-
"[memory.repo_update]",
|
|
823
|
-
'policy = "adaptive" # every-commit / commit-count / daily / pull-request / pull-request-or-daily / adaptive.',
|
|
824
|
-
"commit_threshold = 5 # Pending local commits needed by commit-count and adaptive.",
|
|
825
|
-
"cooldown_hours = 24 # Pending-commit age used by daily, pull-request-or-daily, and adaptive.",
|
|
826
|
-
"",
|
|
827
|
-
"# Local traces may contain prompts, responses, recalled memories, and local paths.",
|
|
828
|
-
"[trace.codex]",
|
|
829
|
-
"enabled = true # Enable local Codex session memory trace collection.",
|
|
830
|
-
"capture_content = true # Store content in local Codex trace events.",
|
|
831
|
-
"",
|
|
832
|
-
"[trace.claude]",
|
|
833
|
-
"enabled = true # Enable local Claude session memory trace collection.",
|
|
834
|
-
"capture_content = true # Store content in local Claude trace events.",
|
|
835
|
-
"",
|
|
836
|
-
"[trace.dsh]",
|
|
837
|
-
"enabled = true # Enable local DSH session memory trace collection.",
|
|
838
|
-
"capture_content = true # Store content in local DSH trace events.",
|
|
839
|
-
"",
|
|
840
|
-
"[trace.opencode]",
|
|
841
|
-
"enabled = true # Enable local OpenCode session memory trace collection.",
|
|
842
|
-
"capture_content = true # Store content in local OpenCode trace events.",
|
|
843
|
-
"",
|
|
844
|
-
].join("\n");
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
function seedMissingMemoraxCodeConfig() {
|
|
848
|
-
const path = memoraxCodeConfigPath();
|
|
849
|
-
return updateConfigFileAtomically({
|
|
850
|
-
path,
|
|
851
|
-
defaultText: defaultMemoraxCodeConfig(),
|
|
852
|
-
transform: (text) => text,
|
|
853
|
-
parseToml: parse,
|
|
854
|
-
warn: (message) => log(message),
|
|
855
|
-
});
|
|
856
|
-
}
|
|
857
|
-
|
|
858
|
-
function setTomlSectionFields(text, section, fields) {
|
|
859
|
-
const source = String(text ?? "");
|
|
860
|
-
const newline = source.includes("\r\n") ? "\r\n" : "\n";
|
|
861
|
-
const lines = source.split(/\r?\n/);
|
|
862
|
-
const escaped = section.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
863
|
-
const header = new RegExp(`^\\s*\\[(?:${escaped}|"${escaped}"|'${escaped}')\\]\\s*(?:#.*)?$`);
|
|
864
|
-
const start = lines.findIndex((line) => header.test(line));
|
|
865
|
-
if (start === -1) {
|
|
866
|
-
const separator = source.length === 0
|
|
867
|
-
? ""
|
|
868
|
-
: source.endsWith(`${newline}${newline}`)
|
|
869
|
-
? ""
|
|
870
|
-
: source.endsWith(newline)
|
|
871
|
-
? newline
|
|
872
|
-
: `${newline}${newline}`;
|
|
873
|
-
return `${source}${separator}[${section}]${newline}${fields.map((field) => field.line).join(newline)}${newline}`;
|
|
874
|
-
}
|
|
875
|
-
|
|
876
|
-
const nextHeader = lines.findIndex((line, index) => index > start && /^\s*\[/.test(line));
|
|
877
|
-
const end = nextHeader === -1 ? lines.length : nextHeader;
|
|
878
|
-
const keys = fields
|
|
879
|
-
.map(({ key }) => key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"))
|
|
880
|
-
.flatMap((key) => [key, `"${key}"`, `'${key}'`])
|
|
881
|
-
.join("|");
|
|
882
|
-
const assignment = new RegExp(`^\\s*#?\\s*(?:${keys})\\s*=`);
|
|
883
|
-
const preservedBody = lines
|
|
884
|
-
.slice(start + 1, end)
|
|
885
|
-
.filter((line) => !assignment.test(line));
|
|
886
|
-
lines.splice(
|
|
887
|
-
start + 1,
|
|
888
|
-
end - start - 1,
|
|
889
|
-
...fields.map((field) => field.line),
|
|
890
|
-
...preservedBody,
|
|
891
|
-
);
|
|
892
|
-
return lines.join(newline);
|
|
893
|
-
}
|
|
894
|
-
|
|
895
|
-
function tomlString(value) {
|
|
896
|
-
return String(value).replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
897
|
-
}
|
|
898
|
-
|
|
899
|
-
function runCommonPreflight() {
|
|
900
|
-
log("Checking local install state...");
|
|
901
|
-
if (updatePostinstall) {
|
|
902
|
-
log("Package update detected; refreshing MemoraX Code assets and checking client availability.");
|
|
903
|
-
}
|
|
904
|
-
const memoraxCodeVersion = runNodeMemoraxCodeCommand(["--version"], { print: false });
|
|
905
|
-
log(`MemoraX Code backend package: ${commandSummary(memoraxCodeVersion) ?? packageVersionSummary()}`);
|
|
906
|
-
if (skipCodexPluginInstall) {
|
|
907
|
-
log("Codex plugin registration is disabled for this npm postinstall; other client setup can still continue.");
|
|
908
|
-
}
|
|
909
|
-
if (skipClaudeAdapterInstall) {
|
|
910
|
-
log("Claude Code adapter setup is disabled for this npm postinstall; other client setup can still continue.");
|
|
911
|
-
}
|
|
912
|
-
if (skipOpenCodeAdapterInstall) {
|
|
913
|
-
log("OpenCode adapter setup is disabled for this npm postinstall; other client setup can still continue.");
|
|
914
|
-
}
|
|
915
|
-
return {};
|
|
916
|
-
}
|
|
917
|
-
|
|
918
|
-
function runCodexPreflight({ integrationSelected = true } = {}) {
|
|
919
|
-
const version = runExternalCommand(codexCommand, ["--version"], { print: false, timeout: 10_000 });
|
|
920
|
-
const runtimeLabel = codexRuntime.source === "app-bundled"
|
|
921
|
-
? "Codex App runtime"
|
|
922
|
-
: codexRuntime.source === "vscode-bundled"
|
|
923
|
-
? "Codex VS Code runtime"
|
|
924
|
-
: "Codex CLI";
|
|
925
|
-
log(`${runtimeLabel}: ${commandSummary(version) ?? "not runnable"}`);
|
|
926
|
-
if (version.status !== 0) {
|
|
927
|
-
return { ok: false, pluginCache: { marketplaceName: CLI_MARKETPLACE_NAME, versions: [] } };
|
|
928
|
-
}
|
|
929
|
-
const pluginCache = installedPluginCache();
|
|
930
|
-
log(`Existing Codex plugin cache: ${pluginCache.versions.length > 0 ? `found (${pluginCache.versions.join(", ")})` : "not installed"}`);
|
|
931
|
-
log(`Codex client process: ${codexClientRunning() ? "running" : "not detected"}`);
|
|
932
|
-
log(integrationSelected
|
|
933
|
-
? "Keeping Codex provider config unchanged and enabling the shared memory hook integration."
|
|
934
|
-
: "Keeping Codex provider config unchanged while checking whether to enable its integration.");
|
|
935
|
-
return { ok: true, pluginCache };
|
|
936
|
-
}
|
|
937
|
-
|
|
938
|
-
function runClaudePreflight({ integrationSelected = true } = {}) {
|
|
939
|
-
const version = runExternalCommand(claudeCommand, ["--version"], { print: false, timeout: 10_000 });
|
|
940
|
-
const runtimeLabel = claudeRuntime.source === "app-bundled"
|
|
941
|
-
? "Claude Code App runtime"
|
|
942
|
-
: claudeRuntime.source === "vscode-bundled"
|
|
943
|
-
? "Claude VS Code runtime"
|
|
944
|
-
: "Claude CLI";
|
|
945
|
-
log(`${runtimeLabel}: ${commandSummary(version) ?? "not runnable"}`);
|
|
946
|
-
if (version.status !== 0) return { ok: false };
|
|
947
|
-
log(integrationSelected
|
|
948
|
-
? "Keeping Claude Code provider config unchanged and enabling the shared memory Hook integration."
|
|
949
|
-
: "Keeping Claude Code provider config unchanged while checking whether to enable its integration.");
|
|
950
|
-
return { ok: true };
|
|
951
|
-
}
|
|
952
|
-
|
|
953
|
-
function runOpenCodePreflight({ integrationSelected = true } = {}) {
|
|
954
|
-
const explicitConfigDir = stringOption(process.env.OPENCODE_CONFIG_DIR);
|
|
955
|
-
const configHome = stringOption(process.env.XDG_CONFIG_HOME) ?? join(homedir(), ".config");
|
|
956
|
-
const configDir = resolve(explicitConfigDir ?? join(configHome, "opencode"));
|
|
957
|
-
const configDetected = explicitConfigDir !== undefined || existsSync(configDir);
|
|
958
|
-
const cliDetected = commandOnPath(
|
|
959
|
-
"opencode",
|
|
960
|
-
process.env.PATH,
|
|
961
|
-
process.platform,
|
|
962
|
-
process.env.PATHEXT,
|
|
963
|
-
);
|
|
964
|
-
const detected = configDetected || cliDetected;
|
|
965
|
-
log(`OpenCode configuration: ${configDetected ? `found (${configDir})` : "not detected"}`);
|
|
966
|
-
log(`OpenCode CLI: ${cliDetected ? "found in PATH" : "not detected"}`);
|
|
967
|
-
if (!detected) return { ok: false };
|
|
968
|
-
log(integrationSelected
|
|
969
|
-
? "Keeping OpenCode provider config unchanged and enabling the shared memory plugin integration."
|
|
970
|
-
: "Keeping OpenCode provider config unchanged while checking whether to enable its integration.");
|
|
971
|
-
return { ok: true };
|
|
972
|
-
}
|
|
973
|
-
|
|
974
|
-
function installedPluginCache() {
|
|
975
|
-
for (const marketplaceName of [CLI_MARKETPLACE_NAME, PERSONAL_MARKETPLACE_NAME]) {
|
|
976
|
-
const versions = installedPluginCacheVersions(marketplaceName);
|
|
977
|
-
if (versions.length > 0) {
|
|
978
|
-
return { marketplaceName, versions };
|
|
979
|
-
}
|
|
980
|
-
}
|
|
981
|
-
return { marketplaceName: CLI_MARKETPLACE_NAME, versions: [] };
|
|
982
|
-
}
|
|
983
|
-
|
|
984
|
-
function installedPluginCacheVersions(marketplaceName) {
|
|
985
|
-
const cacheRoot = join(codexHome(), "plugins", "cache", marketplaceName, PLUGIN_NAME);
|
|
986
|
-
if (!existsSync(cacheRoot)) return [];
|
|
987
|
-
try {
|
|
988
|
-
return readdirSync(cacheRoot, { withFileTypes: true })
|
|
989
|
-
.filter((entry) => entry.isDirectory() && !entry.name.startsWith("."))
|
|
990
|
-
.map((entry) => entry.name)
|
|
991
|
-
.sort();
|
|
992
|
-
} catch {
|
|
993
|
-
return [];
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
|
|
997
|
-
function startBackendAndCheck({
|
|
998
|
-
skipCodexAdapter = false,
|
|
999
|
-
clientMode = "all",
|
|
1000
|
-
codexSkipReason,
|
|
1001
|
-
skipClaudeAdapter = false,
|
|
1002
|
-
claudeAdapterRequired = !skipClaudeAdapter,
|
|
1003
|
-
claudeSkipReason,
|
|
1004
|
-
skipOpenCodeAdapter = false,
|
|
1005
|
-
opencodeAdapterRequired = !skipOpenCodeAdapter,
|
|
1006
|
-
opencodeSkipReason,
|
|
1007
|
-
} = {}) {
|
|
1008
|
-
logGreen("Starting backend with `memorax-code start`...");
|
|
1009
|
-
const adapterFlags = clientLifecycleFlags({ clientMode });
|
|
1010
|
-
const startArgs = ["start", ...adapterFlags];
|
|
1011
|
-
const statusArgs = ["status", ...adapterFlags];
|
|
1012
|
-
let started = runMemoraxCodeCommand(startArgs, pendingClientHookRuntimeEnv());
|
|
1013
|
-
let recovered = false;
|
|
1014
|
-
if (started.status !== 0) {
|
|
1015
|
-
logRed("Backend start failed during npm postinstall.");
|
|
1016
|
-
if (clientHookRuntimeActivationFailed(started)) {
|
|
1017
|
-
logRed("Client Hook runtime activation failed; automatic lifecycle recovery was skipped.");
|
|
1018
|
-
logRed("The previously active runtime remains authoritative.");
|
|
1019
|
-
return { status: "not-verified", dshAdapterEnabled: false };
|
|
1020
|
-
}
|
|
1021
|
-
if (lifecycleLockFailureCode(started)) {
|
|
1022
|
-
logRed("Automatic stop/start recovery is skipped because another MemoraX Code lifecycle command still owns the Backend authority.");
|
|
1023
|
-
printLifecycleLockFailureSuggestions();
|
|
1024
|
-
return { status: "not-verified", dshAdapterEnabled: false };
|
|
1025
|
-
}
|
|
1026
|
-
const runtimeAuthorityFailure = runtimeAuthorityFailureCode(started);
|
|
1027
|
-
if (runtimeAuthorityFailure) {
|
|
1028
|
-
logRed("Automatic stop/start recovery is skipped because persisted Backend runtime authority requires explicit repair.");
|
|
1029
|
-
printRuntimeAuthorityFailureSuggestions(runtimeAuthorityFailure);
|
|
1030
|
-
return { status: "not-verified", dshAdapterEnabled: false };
|
|
1031
|
-
}
|
|
1032
|
-
logRed("Attempting automatic recovery: `memorax-code stop` then `memorax-code start`...");
|
|
1033
|
-
runMemoraxCodeCommand(["stop", ...adapterFlags]);
|
|
1034
|
-
started = runMemoraxCodeCommand(startArgs, pendingClientHookRuntimeEnv());
|
|
1035
|
-
recovered = started.status === 0;
|
|
1036
|
-
if (!recovered) {
|
|
1037
|
-
logRed("Automatic recovery did not start the backend.");
|
|
1038
|
-
logRed("Diagnostic: running `memorax-code status` so the failure is visible.");
|
|
1039
|
-
runMemoraxCodeCommand(statusArgs);
|
|
1040
|
-
printFailureSuggestions();
|
|
1041
|
-
return { status: "not-verified", dshAdapterEnabled: false };
|
|
1042
|
-
}
|
|
1043
|
-
}
|
|
1044
|
-
logGreen(recovered ? "Backend start completed after automatic recovery." : "Backend start completed.");
|
|
1045
|
-
log("Checking backend status with `memorax-code status`...");
|
|
1046
|
-
const checked = runMemoraxCodeCommand(statusArgs);
|
|
1047
|
-
if (checked.status !== 0) {
|
|
1048
|
-
logRed("Backend status check failed during npm postinstall.");
|
|
1049
|
-
printFailureSuggestions();
|
|
1050
|
-
return { status: "not-verified", dshAdapterEnabled: false };
|
|
1051
|
-
}
|
|
1052
|
-
logGreen("Backend status check completed.");
|
|
1053
|
-
const enabled = memoraxCodeEnabled(checked, {
|
|
1054
|
-
codexAdapterRequired: !skipCodexAdapter,
|
|
1055
|
-
claudeAdapterRequired,
|
|
1056
|
-
opencodeAdapterRequired,
|
|
1057
|
-
});
|
|
1058
|
-
if (!enabled) {
|
|
1059
|
-
printUnavailableDiagnostics({ codexSkipReason, claudeSkipReason, opencodeSkipReason });
|
|
1060
|
-
return { status: "unavailable", dshAdapterEnabled: false };
|
|
1061
|
-
}
|
|
1062
|
-
return {
|
|
1063
|
-
status: "enabled",
|
|
1064
|
-
dshAdapterEnabled: dshAdapterReady(checked),
|
|
1065
|
-
};
|
|
1066
|
-
}
|
|
1067
|
-
|
|
1068
|
-
function dshAdapterReady(statusResult) {
|
|
1069
|
-
const output = `${statusResult.stdout ?? ""}\n${statusResult.stderr ?? ""}`;
|
|
1070
|
-
return /\bDSH adapter:\s*ok\b[^\r\n]*\bintegration=plugin\b/im.test(stripAnsi(output));
|
|
1071
|
-
}
|
|
1072
|
-
|
|
1073
|
-
function pendingClientHookRuntimeEnv() {
|
|
1074
|
-
return {
|
|
1075
|
-
MEMORAX_CODE_PENDING_CLIENT_HOOK_RUNTIME_V1: JSON.stringify({
|
|
1076
|
-
version: 1,
|
|
1077
|
-
memoraxCodeHome: resolve(memoraxCodeHome()),
|
|
1078
|
-
generation: {
|
|
1079
|
-
version: stagedHookRuntime.version,
|
|
1080
|
-
runtimeAbi: stagedHookRuntime.runtimeAbi,
|
|
1081
|
-
generationId: stagedHookRuntime.generationId,
|
|
1082
|
-
packageVersion: stagedHookRuntime.packageVersion,
|
|
1083
|
-
contentDigest: stagedHookRuntime.contentDigest,
|
|
1084
|
-
createdAt: stagedHookRuntime.createdAt,
|
|
1085
|
-
},
|
|
1086
|
-
}),
|
|
1087
|
-
};
|
|
1088
|
-
}
|
|
1089
|
-
|
|
1090
|
-
function runtimeAuthorityFailureCode(result) {
|
|
1091
|
-
const output = `${result.stdout ?? ""}\n${result.stderr ?? ""}`;
|
|
1092
|
-
return output.match(/\b(BACKEND_(?:(?:CONNECTION_AUTHORITY|TOKEN_RECORD|SERVICE_STATE)_(?:ABSENT|INVALID|UNSUPPORTED)|SERVICE_STATE_CLEANUP_FAILED))\b/)?.[1];
|
|
1093
|
-
}
|
|
1094
|
-
|
|
1095
|
-
function lifecycleLockFailureCode(result) {
|
|
1096
|
-
const output = `${result.stdout ?? ""}\n${result.stderr ?? ""}`;
|
|
1097
|
-
return output.match(/\bBACKEND_LIFECYCLE_LOCK_TIMEOUT\b/)?.[0];
|
|
1098
|
-
}
|
|
1099
|
-
|
|
1100
|
-
function clientHookRuntimeActivationFailed(result) {
|
|
1101
|
-
const output = `${result.stdout ?? ""}\n${result.stderr ?? ""}`;
|
|
1102
|
-
return /client Hook runtime activation failed:/i.test(output);
|
|
1103
|
-
}
|
|
1104
|
-
|
|
1105
|
-
function clientLifecycleFlags({ clientMode = "all" } = {}) {
|
|
1106
|
-
return ["--clients", clientMode];
|
|
1107
|
-
}
|
|
1108
|
-
|
|
1109
|
-
function clientModeFor(clients, { includeDsh = false } = {}) {
|
|
1110
|
-
const selected = ["codex", "claude", "dsh", "opencode"].filter((client) => (
|
|
1111
|
-
client === "dsh" ? includeDsh : clients.includes(client)
|
|
1112
|
-
));
|
|
1113
|
-
if (selected.length === 4) return "all";
|
|
1114
|
-
return selected.length > 0 ? selected.join(",") : "none";
|
|
1115
|
-
}
|
|
1116
|
-
|
|
1117
|
-
function postinstallClientSkipReason({ explicitlySkipped, selected, enabled }) {
|
|
1118
|
-
if (explicitlySkipped) return "postinstall-skip";
|
|
1119
|
-
if (selected && !enabled) return "client-not-detected";
|
|
1120
|
-
return enabled ? undefined : "client-selection";
|
|
1121
|
-
}
|
|
1122
|
-
|
|
1123
|
-
function clientSelectionMessage(clients, { dshSelected = false } = {}) {
|
|
1124
|
-
if (dshSelected) {
|
|
1125
|
-
const labels = [
|
|
1126
|
-
clients.includes("codex") ? "Codex" : undefined,
|
|
1127
|
-
clients.includes("claude") ? "Claude Code" : undefined,
|
|
1128
|
-
"DeepSeek Harness",
|
|
1129
|
-
clients.includes("opencode") ? "OpenCode" : undefined,
|
|
1130
|
-
].filter(Boolean);
|
|
1131
|
-
return `Configuring MemoraX Code for ${joinedLabels(labels)}.`;
|
|
1132
|
-
}
|
|
1133
|
-
const hasCodex = clients.includes("codex");
|
|
1134
|
-
const hasClaude = clients.includes("claude");
|
|
1135
|
-
const hasOpenCode = clients.includes("opencode");
|
|
1136
|
-
if (hasCodex && hasClaude && hasOpenCode) return "Configuring MemoraX Code for Codex, Claude Code, and OpenCode.";
|
|
1137
|
-
if (hasCodex && hasClaude) return "Configuring MemoraX Code for Codex and Claude Code.";
|
|
1138
|
-
if (hasCodex && hasOpenCode) return "Configuring MemoraX Code for Codex and OpenCode.";
|
|
1139
|
-
if (hasClaude && hasOpenCode) return "Configuring MemoraX Code for Claude Code and OpenCode.";
|
|
1140
|
-
if (hasCodex) return "Configuring MemoraX Code for Codex only.";
|
|
1141
|
-
if (hasClaude) return "Configuring MemoraX Code for Claude Code only.";
|
|
1142
|
-
if (hasOpenCode) return "Configuring MemoraX Code for OpenCode only.";
|
|
1143
|
-
return "Skipping client adapter setup for this npm postinstall.";
|
|
1144
|
-
}
|
|
1145
|
-
|
|
1146
|
-
function clientLabel(client) {
|
|
1147
|
-
if (client === "codex") return "Codex";
|
|
1148
|
-
if (client === "claude") return "Claude Code";
|
|
1149
|
-
return "OpenCode";
|
|
1150
|
-
}
|
|
1151
|
-
|
|
1152
|
-
function detectedClientMessage(clients, dshProfiles = []) {
|
|
1153
|
-
if (clients.length === 0 && dshProfiles.length === 0) {
|
|
1154
|
-
return "No supported client runtime was detected; starting the shared Backend without client adapters.";
|
|
1155
|
-
}
|
|
1156
|
-
if (clients.length === 0) {
|
|
1157
|
-
return "Detected existing DeepSeek Harness profiles; configuring the native DSH plugin and shared Backend.";
|
|
1158
|
-
}
|
|
1159
|
-
return `Detected supported client runtimes. ${clientSelectionMessage(clients)}`;
|
|
1160
|
-
}
|
|
1161
|
-
|
|
1162
|
-
function joinedLabels(labels) {
|
|
1163
|
-
if (labels.length < 2) return labels[0] ?? "";
|
|
1164
|
-
if (labels.length === 2) return `${labels[0]} and ${labels[1]}`;
|
|
1165
|
-
return `${labels.slice(0, -1).join(", ")}, and ${labels.at(-1)}`;
|
|
1166
|
-
}
|
|
1167
|
-
|
|
1168
|
-
function codexHome() {
|
|
1169
|
-
return process.env.CODEX_HOME || join(homedir(), ".codex");
|
|
1170
|
-
}
|
|
1171
|
-
|
|
1172
|
-
function stringOption(value) {
|
|
1173
|
-
if (typeof value !== "string") return undefined;
|
|
1174
|
-
const trimmed = value.trim();
|
|
1175
|
-
return trimmed ? trimmed : undefined;
|
|
1176
|
-
}
|
|
1177
|
-
|
|
1178
|
-
function runMemoraxCodeCommand(args, extraEnv = {}) {
|
|
1179
|
-
return runNodeMemoraxCodeCommand(args, {
|
|
1180
|
-
env: { ...process.env, ...extraEnv, MEMORAX_CODE_BACKEND_SUPPRESS_GUIDANCE: "1" },
|
|
1181
|
-
outputPrefix: BACKEND_PREFIX,
|
|
1182
|
-
});
|
|
1183
|
-
}
|
|
1184
|
-
|
|
1185
|
-
function runNodeMemoraxCodeCommand(args, {
|
|
1186
|
-
env = process.env,
|
|
1187
|
-
print = true,
|
|
1188
|
-
outputPrefix = PREFIX,
|
|
1189
|
-
timeout,
|
|
1190
|
-
} = {}) {
|
|
1191
|
-
return runNodeCliCommand(memoraxCodeBin, args, {
|
|
1192
|
-
env,
|
|
1193
|
-
print,
|
|
1194
|
-
outputPrefix,
|
|
1195
|
-
timeout,
|
|
1196
|
-
});
|
|
1197
|
-
}
|
|
1198
|
-
|
|
1199
|
-
function runNodeMemoraxCliCommand(args, {
|
|
1200
|
-
env = process.env,
|
|
1201
|
-
print = true,
|
|
1202
|
-
outputPrefix = PREFIX,
|
|
1203
|
-
timeout,
|
|
1204
|
-
} = {}) {
|
|
1205
|
-
return runNodeCliCommand(memoraxCliBin, args, {
|
|
1206
|
-
env,
|
|
1207
|
-
print,
|
|
1208
|
-
outputPrefix,
|
|
1209
|
-
timeout,
|
|
1210
|
-
});
|
|
1211
|
-
}
|
|
1212
|
-
|
|
1213
|
-
function runNodeCliCommand(bin, args, options) {
|
|
1214
|
-
return runCommand(process.execPath, [bin, ...args], options);
|
|
1215
|
-
}
|
|
1216
|
-
|
|
1217
|
-
function runExternalCommand(command, args, { env = process.env, print = true, outputPrefix = PREFIX, timeout } = {}) {
|
|
1218
|
-
return runCommand(command, args, { env, print, outputPrefix, timeout });
|
|
1219
|
-
}
|
|
1220
|
-
|
|
1221
|
-
function runCommand(command, args, { env = process.env, print = true, outputPrefix = PREFIX, timeout } = {}) {
|
|
1222
|
-
let invocation;
|
|
1223
|
-
try {
|
|
1224
|
-
invocation = resolveWindowsCliInvocation(command, args, { env });
|
|
1225
|
-
} catch (error) {
|
|
1226
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1227
|
-
if (print) logRed(message);
|
|
1228
|
-
return { status: 1, stdout: "", stderr: message, error };
|
|
1229
|
-
}
|
|
1230
|
-
const result = spawnSync(invocation.command, invocation.args, {
|
|
1231
|
-
env,
|
|
1232
|
-
encoding: "utf8",
|
|
1233
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
1234
|
-
...(timeout ? { timeout } : {}),
|
|
1235
|
-
});
|
|
1236
|
-
if (print) {
|
|
1237
|
-
printCommandOutput(result.stdout, outputPrefix);
|
|
1238
|
-
printCommandOutput(result.stderr, outputPrefix);
|
|
1239
|
-
}
|
|
1240
|
-
if (result.error && print) logRed(`Failed to run \`${[command, ...args].join(" ")}\`: ${result.error.message}`);
|
|
1241
|
-
return result;
|
|
1242
|
-
}
|
|
1243
|
-
|
|
1244
|
-
function printCommandOutput(output, outputPrefix) {
|
|
1245
|
-
for (const line of String(output ?? "").split(/\r?\n/)) {
|
|
1246
|
-
if (!line) continue;
|
|
1247
|
-
if (hasMemoraxCodePrefix(line)) console.warn(line);
|
|
1248
|
-
else logWithPrefix(outputPrefix, line);
|
|
1249
|
-
}
|
|
1250
|
-
}
|
|
1251
|
-
|
|
1252
|
-
function hasMemoraxCodePrefix(line) {
|
|
1253
|
-
return /^\[MemoraX Code (?:Install|Backend)\]: /.test(stripAnsi(line));
|
|
1254
|
-
}
|
|
1255
|
-
|
|
1256
|
-
function stripAnsi(value) {
|
|
1257
|
-
return String(value).replace(/\x1b\[[0-9;]*m/g, "");
|
|
1258
|
-
}
|
|
1259
|
-
|
|
1260
|
-
function firstOutputLine(result) {
|
|
1261
|
-
const output = `${result.stdout ?? ""}\n${result.stderr ?? ""}`.split(/\r?\n/).map((line) => line.trim()).find(Boolean);
|
|
1262
|
-
return output || undefined;
|
|
1263
|
-
}
|
|
1264
|
-
|
|
1265
|
-
function commandSummary(result) {
|
|
1266
|
-
if (result.status !== 0) return undefined;
|
|
1267
|
-
return firstOutputLine(result);
|
|
1268
|
-
}
|
|
1269
|
-
|
|
1270
|
-
function packageVersionSummary() {
|
|
1271
|
-
try {
|
|
1272
|
-
const pkg = JSON.parse(readFileSync(join(dirname(scriptDir), "package.json"), "utf8"));
|
|
1273
|
-
if (typeof pkg.version === "string" && pkg.version) return `installed (${pkg.version}), command version unavailable`;
|
|
1274
|
-
} catch {
|
|
1275
|
-
// Ignore package metadata errors; this is a best-effort install preflight.
|
|
1276
|
-
}
|
|
1277
|
-
return "installed, version unavailable";
|
|
1278
|
-
}
|
|
1279
|
-
|
|
1280
|
-
function codexClientRunning() {
|
|
1281
|
-
const result = process.platform === "win32"
|
|
1282
|
-
? runExternalCommand("tasklist.exe", ["/FO", "CSV", "/NH"], { print: false })
|
|
1283
|
-
: runExternalCommand("ps", ["-axo", "comm=,args="], { print: false });
|
|
1284
|
-
if (result.status !== 0) return false;
|
|
1285
|
-
return String(result.stdout ?? "").split(/\r?\n/).some((line) => {
|
|
1286
|
-
const lower = line.toLowerCase();
|
|
1287
|
-
if (!lower.includes("codex")) return false;
|
|
1288
|
-
if (lower.includes("memorax-code-plugin-postinstall")) return false;
|
|
1289
|
-
return process.platform === "win32"
|
|
1290
|
-
? /"codex(?:\.exe)?"/i.test(line)
|
|
1291
|
-
: lower.includes("codex.app") || /(^|[/\s])codex(\s|$)/i.test(line);
|
|
1292
|
-
});
|
|
1293
|
-
}
|
|
15
|
+
const scriptDir = dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
const memoraxCodeHome = process.env.MEMORAX_CODE_HOME?.trim() || join(homedir(), ".memorax-code");
|
|
1294
17
|
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
opencodeAdapterEnabled = true,
|
|
1300
|
-
} = {}) {
|
|
1301
|
-
const hookClientText = enabledClientText({
|
|
1302
|
-
codexAdapterEnabled,
|
|
1303
|
-
claudeAdapterEnabled,
|
|
1304
|
-
opencodeAdapterEnabled,
|
|
18
|
+
try {
|
|
19
|
+
const result = await runNpmPostinstallPackageTransition({
|
|
20
|
+
memoraxCodeHome,
|
|
21
|
+
memoraxCodeBin: join(scriptDir, "memorax-code.mjs"),
|
|
1305
22
|
});
|
|
1306
|
-
if (
|
|
1307
|
-
|
|
1308
|
-
log(`Restart or refresh ${hookClientText} only if its plugin shell was installed, changed, or newly enabled, or if MemoraX Code is not active on the next prompt.`);
|
|
1309
|
-
} else if (hookClientText) {
|
|
1310
|
-
logGreen(`${bold(`Restart or refresh ${hookClientText}`)} before opening a new MemoraX Code session.`);
|
|
1311
|
-
}
|
|
1312
|
-
if (dshAdapterEnabled) {
|
|
1313
|
-
logGreen(`${bold("Restart or refresh DeepSeek Harness")} so its Profiles load the installed MemoraX Code plugin.`);
|
|
1314
|
-
} else if (!hookClientText) {
|
|
1315
|
-
logGreen(`${bold("MemoraX Code backend is running")}; client adapters were skipped for this install.`);
|
|
1316
|
-
}
|
|
1317
|
-
if (codexAdapterEnabled && !updatePostinstall) {
|
|
1318
|
-
logGreen(`After restart, ${bold("enable the MemoraX Code Codex Adapter plugin")} from Codex Plugins or CLI \`/plugins\` if it is not already enabled.`);
|
|
1319
|
-
}
|
|
1320
|
-
const statusCommands = statusCommandText({ codexAdapterEnabled, claudeAdapterEnabled, opencodeAdapterEnabled });
|
|
1321
|
-
if (hookClientText || !dshAdapterEnabled) {
|
|
1322
|
-
log(`If MemoraX Code is not active ${updatePostinstall ? "on the next prompt" : "in new sessions"}, run ${statusCommands}.`);
|
|
1323
|
-
}
|
|
1324
|
-
if (dshAdapterEnabled) {
|
|
1325
|
-
log("If MemoraX Code is not active after restarting or refreshing DeepSeek Harness, run `memorax-code status`.");
|
|
1326
|
-
}
|
|
1327
|
-
log("If npm hides install details, reinstall with `--foreground-scripts`.");
|
|
1328
|
-
}
|
|
1329
|
-
|
|
1330
|
-
function enabledClientText({
|
|
1331
|
-
codexAdapterEnabled = true,
|
|
1332
|
-
claudeAdapterEnabled = true,
|
|
1333
|
-
opencodeAdapterEnabled = true,
|
|
1334
|
-
} = {}) {
|
|
1335
|
-
const labels = [
|
|
1336
|
-
codexAdapterEnabled ? "Codex" : undefined,
|
|
1337
|
-
claudeAdapterEnabled ? "Claude Code" : undefined,
|
|
1338
|
-
opencodeAdapterEnabled ? "OpenCode" : undefined,
|
|
1339
|
-
].filter(Boolean);
|
|
1340
|
-
if (labels.length < 2) return labels[0] ?? "";
|
|
1341
|
-
if (labels.length === 2) return `${labels[0]} or ${labels[1]}`;
|
|
1342
|
-
return `${labels.slice(0, -1).join(", ")}, or ${labels.at(-1)}`;
|
|
1343
|
-
}
|
|
1344
|
-
|
|
1345
|
-
function statusCommandText({
|
|
1346
|
-
codexAdapterEnabled = true,
|
|
1347
|
-
claudeAdapterEnabled = true,
|
|
1348
|
-
opencodeAdapterEnabled = true,
|
|
1349
|
-
} = {}) {
|
|
1350
|
-
const commands = ["`memorax-code status`"];
|
|
1351
|
-
if (codexAdapterEnabled) commands.push("`memorax-code-codex status`");
|
|
1352
|
-
if (claudeAdapterEnabled) commands.push("`memorax-code-claude status`");
|
|
1353
|
-
if (opencodeAdapterEnabled) commands.push("`memorax-code-opencode status`");
|
|
1354
|
-
if (commands.length === 1) return commands[0];
|
|
1355
|
-
if (commands.length === 2) return `${commands[0]} and ${commands[1]}`;
|
|
1356
|
-
return `${commands.slice(0, -1).join(", ")}, and ${commands.at(-1)}`;
|
|
1357
|
-
}
|
|
1358
|
-
|
|
1359
|
-
function printPostinstallSummary(backendAndAdaptersStatus) {
|
|
1360
|
-
log("Package: Installed");
|
|
1361
|
-
const backendStatusLabel = backendAndAdaptersStatus === "enabled"
|
|
1362
|
-
? blueBold("Enabled")
|
|
1363
|
-
: backendAndAdaptersStatus === "unavailable"
|
|
1364
|
-
? redBold("Unavailable")
|
|
1365
|
-
: "Not verified";
|
|
1366
|
-
log(`Backend and selected adapters: ${backendStatusLabel}`);
|
|
1367
|
-
const memoraxStatus = readMemoraxInstallStatus();
|
|
1368
|
-
if (!memoraxStatus) {
|
|
1369
|
-
logRed("MemoraX memory: Status unavailable");
|
|
1370
|
-
log("Run `memorax-cli status` after installation to inspect the effective configuration.");
|
|
1371
|
-
return;
|
|
1372
|
-
}
|
|
1373
|
-
if (!memoraxStatus.configured) {
|
|
1374
|
-
log("MemoraX memory: Not configured");
|
|
1375
|
-
log(`Package installed, MemoraX not configured. MemoraX Code memory remains unavailable until you connect an account from ${MEMORAX_ACCOUNT_URL}.`);
|
|
1376
|
-
return;
|
|
1377
|
-
}
|
|
1378
|
-
log(`MemoraX memory: ${blueBold("Configured")}`);
|
|
1379
|
-
if (memoraxStatus.writebackEnabled) {
|
|
1380
|
-
log(`Automatic writeback: ${blueBold("Enabled")}`);
|
|
1381
|
-
} else if (!memoraxStatus.globalWritebackEnabled) {
|
|
1382
|
-
log("Automatic writeback: Disabled by the global kill switch");
|
|
1383
|
-
} else {
|
|
1384
|
-
log("Automatic writeback: Disabled by effective configuration");
|
|
23
|
+
if (result.disposition === "restored") {
|
|
24
|
+
console.warn(`${PREFIX} Updated managed Backend started and verified.`);
|
|
1385
25
|
}
|
|
26
|
+
} catch (error) {
|
|
27
|
+
printCommandOutput(error?.command);
|
|
28
|
+
console.error(`${PREFIX} Package transition could not be completed: ${error instanceof Error ? error.message : String(error)}`);
|
|
29
|
+
process.exit(1);
|
|
1386
30
|
}
|
|
1387
31
|
|
|
1388
|
-
function
|
|
1389
|
-
const
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
);
|
|
1393
|
-
if (result.error || result.signal || (result.status !== 0 && result.status !== 1)) {
|
|
1394
|
-
return undefined;
|
|
1395
|
-
}
|
|
1396
|
-
try {
|
|
1397
|
-
const report = JSON.parse(String(result.stdout ?? ""));
|
|
1398
|
-
if (!isRecord(report)
|
|
1399
|
-
|| report.action !== "memory.status"
|
|
1400
|
-
|| report.provider !== "memory.memorax"
|
|
1401
|
-
|| typeof report.ok !== "boolean"
|
|
1402
|
-
|| !isRecord(report.config)
|
|
1403
|
-
|| typeof report.config.configured !== "boolean") {
|
|
1404
|
-
return undefined;
|
|
1405
|
-
}
|
|
1406
|
-
const configuredResult = result.status === 0
|
|
1407
|
-
&& report.ok === true
|
|
1408
|
-
&& report.config.configured === true;
|
|
1409
|
-
const unconfiguredResult = result.status === 1
|
|
1410
|
-
&& report.ok === false
|
|
1411
|
-
&& report.config.configured === false;
|
|
1412
|
-
if (!configuredResult && !unconfiguredResult) {
|
|
1413
|
-
return undefined;
|
|
1414
|
-
}
|
|
1415
|
-
if (unconfiguredResult) {
|
|
1416
|
-
return { configured: false };
|
|
32
|
+
function printCommandOutput(command) {
|
|
33
|
+
for (const output of [command?.stdout, command?.stderr]) {
|
|
34
|
+
for (const line of String(output ?? "").split(/\r?\n/)) {
|
|
35
|
+
if (line) console.warn(`${PREFIX} ${line}`);
|
|
1417
36
|
}
|
|
1418
|
-
if (!isRecord(report.config.writeback)
|
|
1419
|
-
|| typeof report.config.writeback.globalEnabled !== "boolean"
|
|
1420
|
-
|| typeof report.config.writeback.writebackEnabled !== "boolean") {
|
|
1421
|
-
return undefined;
|
|
1422
|
-
}
|
|
1423
|
-
return {
|
|
1424
|
-
configured: true,
|
|
1425
|
-
globalWritebackEnabled: report.config.writeback.globalEnabled,
|
|
1426
|
-
writebackEnabled: report.config.writeback.writebackEnabled,
|
|
1427
|
-
};
|
|
1428
|
-
} catch {
|
|
1429
|
-
return undefined;
|
|
1430
|
-
}
|
|
1431
|
-
}
|
|
1432
|
-
|
|
1433
|
-
function printUnavailableDiagnostics({ codexSkipReason, claudeSkipReason, opencodeSkipReason } = {}) {
|
|
1434
|
-
logRed("MemoraX Code is not enabled for new client sessions.");
|
|
1435
|
-
logRed("Check `memorax-code status`, `memorax-code-codex status`, `memorax-code-claude status`, and `memorax-code-opencode status` for Backend and adapter details.");
|
|
1436
|
-
logRed("If Codex, Claude Code, or OpenCode is open, restart or refresh it after fixing the reported status.");
|
|
1437
|
-
if (codexSkipReason) printCodexSkippedDiagnostics(codexSkipReason);
|
|
1438
|
-
if (claudeSkipReason) printClaudeSkippedDiagnostics(claudeSkipReason);
|
|
1439
|
-
if (opencodeSkipReason) printOpenCodeSkippedDiagnostics(opencodeSkipReason);
|
|
1440
|
-
printCommonCommands({
|
|
1441
|
-
codexAdapterEnabled: !codexSkipReason,
|
|
1442
|
-
claudeAdapterEnabled: !claudeSkipReason,
|
|
1443
|
-
opencodeAdapterEnabled: !opencodeSkipReason,
|
|
1444
|
-
});
|
|
1445
|
-
}
|
|
1446
|
-
|
|
1447
|
-
function printCodexSkippedDiagnostics(reason) {
|
|
1448
|
-
logRed("Codex plugin registration was skipped for this npm postinstall, so MemoraX Code left the Codex plugin unchanged.");
|
|
1449
|
-
log("Claude Code can still use MemoraX Code when `memorax-code-claude status` reports the Hook integration is enabled.");
|
|
1450
|
-
log("Run `memorax-code start` after installing the Codex plugin, then restart or refresh Codex.");
|
|
1451
|
-
}
|
|
1452
|
-
|
|
1453
|
-
function printClaudeSkippedDiagnostics() {
|
|
1454
|
-
logRed("Claude Code adapter setup was skipped for this npm postinstall, so MemoraX Code left the Claude integration unchanged.");
|
|
1455
|
-
log("Codex can still use MemoraX Code when `memorax-code-codex status` reports the adapter is enabled.");
|
|
1456
|
-
log("Run `memorax-code start` after installing Claude Code, then restart or refresh Claude Code.");
|
|
1457
|
-
}
|
|
1458
|
-
|
|
1459
|
-
function printOpenCodeSkippedDiagnostics() {
|
|
1460
|
-
logRed("OpenCode adapter setup was skipped for this npm postinstall, so MemoraX Code left the OpenCode integration unchanged.");
|
|
1461
|
-
log("Run `memorax-code start --clients opencode` after installing OpenCode, then restart or refresh OpenCode.");
|
|
1462
|
-
}
|
|
1463
|
-
|
|
1464
|
-
function printFailureSuggestions() {
|
|
1465
|
-
logRed("Suggested recovery: run `memorax-code stop`, then `memorax-code start`, then `memorax-code status`.");
|
|
1466
|
-
logRed("If the Backend port is busy, stop the process using 127.0.0.1:8787 and retry `memorax-code start`.");
|
|
1467
|
-
logRed("If client sessions still bypass MemoraX Code, restart or refresh the affected client and verify the relevant adapter status.");
|
|
1468
|
-
printCommonCommands();
|
|
1469
|
-
}
|
|
1470
|
-
|
|
1471
|
-
function printRuntimeAuthorityFailureSuggestions(code) {
|
|
1472
|
-
if (code.startsWith("BACKEND_CONNECTION_AUTHORITY_")) {
|
|
1473
|
-
logRed("Confirm the intended local Backend bind, then run `memorax-code start --host 127.0.0.1 --port <intended-port>`.");
|
|
1474
|
-
logRed("The connection authority is replaced only after that Backend starts successfully; then run `memorax-code status`.");
|
|
1475
|
-
} else if (code.startsWith("BACKEND_TOKEN_RECORD_")) {
|
|
1476
|
-
logRed("Inspect `memorax-code status`; after safely stopping the managed Backend, run `memorax-code token --rotate`, then `memorax-code start`.");
|
|
1477
|
-
} else {
|
|
1478
|
-
logRed("Inspect `memorax-code status` and the reported backend.pid.json path; confirm process ownership before repairing or removing that state file.");
|
|
1479
37
|
}
|
|
1480
|
-
printCommonCommands();
|
|
1481
|
-
}
|
|
1482
|
-
|
|
1483
|
-
function printLifecycleLockFailureSuggestions() {
|
|
1484
|
-
logRed("Let the existing lifecycle command finish, then run `memorax-code status`.");
|
|
1485
|
-
logRed("If MemoraX Code is not ready, retry `memorax-code start`; do not stop a Backend started by the concurrent owner.");
|
|
1486
|
-
printCommonCommands();
|
|
1487
|
-
}
|
|
1488
|
-
|
|
1489
|
-
function printCommonCommands({
|
|
1490
|
-
codexAdapterEnabled = true,
|
|
1491
|
-
claudeAdapterEnabled = true,
|
|
1492
|
-
opencodeAdapterEnabled = true,
|
|
1493
|
-
} = {}) {
|
|
1494
|
-
log("Common commands:");
|
|
1495
|
-
log("- `memorax-code status`: check the local backend and adapter state.");
|
|
1496
|
-
log("- `memorax-cli status`: check required MemoraX configuration and effective memory switches.");
|
|
1497
|
-
log("- `memorax-code start`: start or refresh the local memory backend and client integrations.");
|
|
1498
|
-
log("- `memorax-code stop`: stop the local memory backend and disable managed client integrations.");
|
|
1499
|
-
if (codexAdapterEnabled) log("- `memorax-code-codex sessions`: verify recent native Codex session registration.");
|
|
1500
|
-
if (claudeAdapterEnabled) log("- `memorax-code-claude sessions`: verify recent native Claude Code session registration.");
|
|
1501
|
-
if (opencodeAdapterEnabled) log("- `memorax-code-opencode doctor`: verify the managed OpenCode plugin, runtime evidence, and Backend health.");
|
|
1502
|
-
}
|
|
1503
|
-
|
|
1504
|
-
function memoraxCodeEnabled(statusResult, {
|
|
1505
|
-
codexAdapterRequired = true,
|
|
1506
|
-
claudeAdapterRequired = true,
|
|
1507
|
-
opencodeAdapterRequired = true,
|
|
1508
|
-
} = {}) {
|
|
1509
|
-
const output = `${statusResult.stdout ?? ""}\n${statusResult.stderr ?? ""}`;
|
|
1510
|
-
const normalized = stripAnsi(output);
|
|
1511
|
-
const backendOk = /MemoraX Code Backend status:\s*Enabled\b/im.test(normalized)
|
|
1512
|
-
|| /MemoraX Code status:\s*Enabled\b/im.test(normalized)
|
|
1513
|
-
|| /memorax-code:\s*ok\b/im.test(normalized);
|
|
1514
|
-
const serviceOk = /Backend status:\s*Enabled\b/im.test(normalized)
|
|
1515
|
-
|| /Backend:\s*(?:running|ok)\b/im.test(normalized);
|
|
1516
|
-
const codexAdapterOk = /Codex adapter:\s*ok\b/im.test(normalized);
|
|
1517
|
-
const claudeAdapterOk = /Claude adapter:\s*ok\b/im.test(normalized);
|
|
1518
|
-
const opencodeAdapterOk = /OpenCode adapter:\s*ok\b/im.test(normalized);
|
|
1519
|
-
return backendOk
|
|
1520
|
-
&& serviceOk
|
|
1521
|
-
&& (!codexAdapterRequired || codexAdapterOk)
|
|
1522
|
-
&& (!claudeAdapterRequired || claudeAdapterOk)
|
|
1523
|
-
&& (!opencodeAdapterRequired || opencodeAdapterOk);
|
|
1524
|
-
}
|
|
1525
|
-
|
|
1526
|
-
function log(message) {
|
|
1527
|
-
logWithPrefix(PREFIX, message);
|
|
1528
|
-
}
|
|
1529
|
-
|
|
1530
|
-
function logGreen(message) {
|
|
1531
|
-
console.warn(`${GREEN}${PREFIX} ${message}${RESET}`);
|
|
1532
|
-
}
|
|
1533
|
-
|
|
1534
|
-
function logRed(message) {
|
|
1535
|
-
console.warn(`${RED}${PREFIX} ${message}${RESET}`);
|
|
1536
|
-
}
|
|
1537
|
-
|
|
1538
|
-
function logWithPrefix(prefix, message) {
|
|
1539
|
-
console.warn(`${prefix} ${message}`);
|
|
1540
|
-
}
|
|
1541
|
-
|
|
1542
|
-
function bold(message) {
|
|
1543
|
-
return `${BOLD}${message}\x1b[22m`;
|
|
1544
|
-
}
|
|
1545
|
-
|
|
1546
|
-
function blueBold(message) {
|
|
1547
|
-
return `${BLUE}${bold(message)}${RESET}`;
|
|
1548
|
-
}
|
|
1549
|
-
|
|
1550
|
-
function redBold(message) {
|
|
1551
|
-
return `${RED}${bold(message)}${RESET}`;
|
|
1552
38
|
}
|