superlocalmemory 4.1.24 → 4.2.0
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/.claude-plugin/marketplace.json +3 -3
- package/CHANGELOG.md +297 -0
- package/README.md +206 -271
- package/antigravity-plugin/_GENERATED.md +2 -1
- package/antigravity-plugin/agents/slm-governance-advisor.md +10 -4
- package/antigravity-plugin/agents/slm-loop-runner.md +1 -1
- package/antigravity-plugin/agents/slm-memory-advisor.md +8 -4
- package/antigravity-plugin/agents/slm-optimize-advisor.md +1 -1
- package/antigravity-plugin/plugin.json +1 -1
- package/antigravity-plugin/skills/slm-bot-memory/SKILL.md +36 -1
- package/antigravity-plugin/skills/slm-cache/SKILL.md +1 -1
- package/antigravity-plugin/skills/slm-compress/SKILL.md +1 -1
- package/antigravity-plugin/skills/slm-getting-started-bot/SKILL.md +58 -7
- package/antigravity-plugin/skills/slm-governance/SKILL.md +61 -5
- package/antigravity-plugin/skills/slm-graph/SKILL.md +1 -1
- package/antigravity-plugin/skills/slm-loop/SKILL.md +22 -2
- package/antigravity-plugin/skills/slm-media/SKILL.md +334 -0
- package/antigravity-plugin/skills/slm-mesh/SKILL.md +110 -8
- package/antigravity-plugin/skills/slm-profile/SKILL.md +17 -12
- package/antigravity-plugin/skills/slm-recall/SKILL.md +91 -3
- package/antigravity-plugin/skills/slm-remember/SKILL.md +6 -1
- package/antigravity-plugin/skills/slm-scope/SKILL.md +1 -1
- package/antigravity-plugin/skills/slm-session/SKILL.md +1 -1
- package/antigravity-plugin/skills/slm-status/SKILL.md +48 -2
- package/antigravity-plugin/skills/slm-web-access/SKILL.md +92 -6
- package/bin/slm-npm +1 -1
- package/codex-plugin/.claude-plugin/plugin.json +1 -1
- package/codex-plugin/.codex-plugin/plugin.json +1 -1
- package/codex-plugin/AGENTS.md +38 -9
- package/codex-plugin/README.md +8 -6
- package/codex-plugin/_GENERATED.md +2 -1
- package/codex-plugin/agents/slm-governance-advisor.md +10 -4
- package/codex-plugin/agents/slm-loop-runner.md +1 -1
- package/codex-plugin/agents/slm-memory-advisor.md +8 -4
- package/codex-plugin/agents/slm-optimize-advisor.md +1 -1
- package/codex-plugin/skills/slm-bot-memory/SKILL.md +36 -1
- package/codex-plugin/skills/slm-cache/SKILL.md +1 -1
- package/codex-plugin/skills/slm-compress/SKILL.md +1 -1
- package/codex-plugin/skills/slm-getting-started-bot/SKILL.md +58 -7
- package/codex-plugin/skills/slm-governance/SKILL.md +61 -5
- package/codex-plugin/skills/slm-graph/SKILL.md +1 -1
- package/codex-plugin/skills/slm-loop/SKILL.md +22 -2
- package/codex-plugin/skills/slm-media/SKILL.md +334 -0
- package/codex-plugin/skills/slm-mesh/SKILL.md +110 -8
- package/codex-plugin/skills/slm-profile/SKILL.md +17 -12
- package/codex-plugin/skills/slm-recall/SKILL.md +91 -3
- package/codex-plugin/skills/slm-remember/SKILL.md +6 -1
- package/codex-plugin/skills/slm-scope/SKILL.md +1 -1
- package/codex-plugin/skills/slm-session/SKILL.md +1 -1
- package/codex-plugin/skills/slm-status/SKILL.md +48 -2
- package/codex-plugin/skills/slm-web-access/SKILL.md +92 -6
- package/copilot-plugin/.github/agents/slm-governance-advisor.agent.md +11 -5
- package/copilot-plugin/.github/agents/slm-loop-runner.agent.md +2 -2
- package/copilot-plugin/.github/agents/slm-memory-advisor.agent.md +9 -5
- package/copilot-plugin/.github/agents/slm-optimize-advisor.agent.md +2 -2
- package/copilot-plugin/.github/copilot-instructions.md +14 -5
- package/copilot-plugin/.github/prompts/slm-bot-memory.prompt.md +35 -2
- package/copilot-plugin/.github/prompts/slm-cache.prompt.md +2 -2
- package/copilot-plugin/.github/prompts/slm-compress.prompt.md +2 -2
- package/copilot-plugin/.github/prompts/slm-getting-started-bot.prompt.md +57 -8
- package/copilot-plugin/.github/prompts/slm-governance.prompt.md +60 -5
- package/copilot-plugin/.github/prompts/slm-graph.prompt.md +2 -2
- package/copilot-plugin/.github/prompts/slm-loop.prompt.md +28 -2
- package/copilot-plugin/.github/prompts/slm-media.prompt.md +335 -0
- package/copilot-plugin/.github/prompts/slm-mesh.prompt.md +108 -8
- package/copilot-plugin/.github/prompts/slm-profile.prompt.md +18 -13
- package/copilot-plugin/.github/prompts/slm-recall.prompt.md +92 -3
- package/copilot-plugin/.github/prompts/slm-remember.prompt.md +7 -2
- package/copilot-plugin/.github/prompts/slm-scope.prompt.md +2 -2
- package/copilot-plugin/.github/prompts/slm-session.prompt.md +2 -2
- package/copilot-plugin/.github/prompts/slm-status.prompt.md +48 -2
- package/copilot-plugin/.github/prompts/slm-web-access.prompt.md +92 -6
- package/copilot-plugin/README.md +3 -3
- package/copilot-plugin/_GENERATED.md +2 -1
- package/hermes-plugin/__init__.py +15 -5
- package/hermes-plugin/agents/slm-governance-advisor.md +10 -4
- package/hermes-plugin/agents/slm-loop-runner.md +1 -1
- package/hermes-plugin/agents/slm-memory-advisor.md +8 -4
- package/hermes-plugin/agents/slm-optimize-advisor.md +1 -1
- package/hermes-plugin/command-inventory.json +3 -3
- package/hermes-plugin/plugin.yaml +1 -1
- package/hermes-plugin/skills/slm-bot-memory/SKILL.md +36 -1
- package/hermes-plugin/skills/slm-cache/SKILL.md +1 -1
- package/hermes-plugin/skills/slm-compress/SKILL.md +1 -1
- package/hermes-plugin/skills/slm-getting-started-bot/SKILL.md +58 -7
- package/hermes-plugin/skills/slm-governance/SKILL.md +61 -5
- package/hermes-plugin/skills/slm-graph/SKILL.md +1 -1
- package/hermes-plugin/skills/slm-loop/SKILL.md +22 -2
- package/hermes-plugin/skills/slm-media/SKILL.md +334 -0
- package/hermes-plugin/skills/slm-mesh/SKILL.md +110 -8
- package/hermes-plugin/skills/slm-profile/SKILL.md +17 -12
- package/hermes-plugin/skills/slm-recall/SKILL.md +91 -3
- package/hermes-plugin/skills/slm-remember/SKILL.md +6 -1
- package/hermes-plugin/skills/slm-scope/SKILL.md +1 -1
- package/hermes-plugin/skills/slm-session/SKILL.md +1 -1
- package/hermes-plugin/skills/slm-status/SKILL.md +48 -2
- package/hermes-plugin/skills/slm-web-access/SKILL.md +92 -6
- package/ide/configs/cody-commands.json +1 -1
- package/ide/configs/continue-mcp.yaml +9 -14
- package/ide/configs/continue-skills.yaml +8 -3
- package/ide/configs/opencode-mcp.json +4 -5
- package/package.json +38 -21
- package/plugin/.claude-plugin/plugin.json +2 -2
- package/plugin/.cursor-plugin/plugin.json +1 -1
- package/plugin/CLAUDE.md +13 -4
- package/plugin/agents/slm-governance-advisor.md +10 -4
- package/plugin/agents/slm-loop-runner.md +1 -1
- package/plugin/agents/slm-memory-advisor.md +8 -4
- package/plugin/agents/slm-optimize-advisor.md +1 -1
- package/plugin/cursor-skills/slm-bot-memory/SKILL.md +36 -1
- package/plugin/cursor-skills/slm-getting-started-bot/SKILL.md +58 -7
- package/plugin/cursor-skills/slm-recall/SKILL.md +91 -3
- package/plugin/cursor-skills/slm-remember/SKILL.md +6 -1
- package/plugin/cursor-skills/slm-scope/SKILL.md +1 -1
- package/plugin/cursor-skills/slm-session/SKILL.md +1 -1
- package/plugin/mcp.cursor.json +1 -1
- package/plugin/requirements.txt +1 -1
- package/plugin/skills/slm-bot-memory/SKILL.md +36 -1
- package/plugin/skills/slm-cache/SKILL.md +1 -1
- package/plugin/skills/slm-compress/SKILL.md +1 -1
- package/plugin/skills/slm-getting-started-bot/SKILL.md +58 -7
- package/plugin/skills/slm-governance/SKILL.md +61 -5
- package/plugin/skills/slm-graph/SKILL.md +1 -1
- package/plugin/skills/slm-loop/SKILL.md +22 -2
- package/plugin/skills/slm-media/SKILL.md +334 -0
- package/plugin/skills/slm-mesh/SKILL.md +110 -8
- package/plugin/skills/slm-profile/SKILL.md +17 -12
- package/plugin/skills/slm-recall/SKILL.md +91 -3
- package/plugin/skills/slm-remember/SKILL.md +6 -1
- package/plugin/skills/slm-scope/SKILL.md +1 -1
- package/plugin/skills/slm-session/SKILL.md +1 -1
- package/plugin/skills/slm-status/SKILL.md +48 -2
- package/plugin/skills/slm-web-access/SKILL.md +92 -6
- package/plugin-src/agents/slm-governance-advisor.md +10 -4
- package/plugin-src/agents/slm-loop-runner.md +1 -1
- package/plugin-src/agents/slm-memory-advisor.md +8 -4
- package/plugin-src/agents/slm-optimize-advisor.md +1 -1
- package/plugin-src/rules/AGENTS.md +38 -9
- package/plugin-src/rules/CLAUDE.md.fragment +13 -4
- package/plugin-src/skills/slm-bot-memory/SKILL.md +36 -1
- package/plugin-src/skills/slm-cache/SKILL.md +1 -1
- package/plugin-src/skills/slm-compress/SKILL.md +1 -1
- package/plugin-src/skills/slm-getting-started-bot/SKILL.md +58 -7
- package/plugin-src/skills/slm-governance/SKILL.md +61 -5
- package/plugin-src/skills/slm-graph/SKILL.md +1 -1
- package/plugin-src/skills/slm-loop/SKILL.md +22 -2
- package/plugin-src/skills/slm-media/SKILL.md +334 -0
- package/plugin-src/skills/slm-mesh/SKILL.md +110 -8
- package/plugin-src/skills/slm-profile/SKILL.md +17 -12
- package/plugin-src/skills/slm-recall/SKILL.md +91 -3
- package/plugin-src/skills/slm-remember/SKILL.md +6 -1
- package/plugin-src/skills/slm-scope/SKILL.md +1 -1
- package/plugin-src/skills/slm-session/SKILL.md +1 -1
- package/plugin-src/skills/slm-status/SKILL.md +48 -2
- package/plugin-src/skills/slm-web-access/SKILL.md +92 -6
- package/scripts/postinstall/engine-upgrade.js +112 -0
- package/scripts/postinstall/media-request.js +203 -0
- package/scripts/postinstall/node-arch.js +27 -0
- package/scripts/postinstall-interactive.js +15 -19
- package/scripts/postinstall.js +99 -13
- package/scripts/preuninstall.js +55 -3
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What's-new banner and the "turn on images and documents" request.
|
|
3
|
+
*
|
|
4
|
+
* The installer never downloads anything. It only records a request in
|
|
5
|
+
* <data root>/features.json; the SLM daemon acts on it the next time it
|
|
6
|
+
* starts. Nothing is written unless the person asked (a flag, the
|
|
7
|
+
* SLM_ENABLE_MEDIA=1 environment variable, or a "yes" at a terminal).
|
|
8
|
+
*
|
|
9
|
+
* Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
|
|
10
|
+
* Licensed under AGPL-3.0-or-later.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
'use strict';
|
|
14
|
+
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const path = require('path');
|
|
17
|
+
const os = require('os');
|
|
18
|
+
const readline = require('readline');
|
|
19
|
+
const { nodeRunsTranslated } = require('./node-arch.js');
|
|
20
|
+
|
|
21
|
+
const FEATURES_FILE = 'features.json';
|
|
22
|
+
|
|
23
|
+
const GIB = 1024 ** 3;
|
|
24
|
+
// Same threshold and override as the Python side (MEDIA_MIN_RAM_BYTES and
|
|
25
|
+
// LOW_RAM_OVERRIDE_ENV in runtimes/media_env.py): 15 GiB, because machines sold as
|
|
26
|
+
// 16 GB report a little less.
|
|
27
|
+
const MIN_RAM_BYTES = 15 * GIB;
|
|
28
|
+
const LOW_RAM_OVERRIDE_ENV = 'SLM_MEDIA_ALLOW_LOW_RAM';
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Why images and documents cannot be turned on here, or '' when they can.
|
|
32
|
+
* Unknown memory (0) is allowed; SLM_MEDIA_ALLOW_LOW_RAM=1 is the developer override.
|
|
33
|
+
*/
|
|
34
|
+
function ramRefusal(totalBytes, env = process.env) {
|
|
35
|
+
if (!(totalBytes > 0 && totalBytes < MIN_RAM_BYTES)) return '';
|
|
36
|
+
if (env && env[LOW_RAM_OVERRIDE_ENV] === '1') return '';
|
|
37
|
+
return 'Images and documents need a computer with at least 16 GB of memory; this one has '
|
|
38
|
+
+ (totalBytes / GIB).toFixed(1) + ' GB. Your text memories keep working.';
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Where the managed picture and document environment has a build (the same list as
|
|
42
|
+
// runtimes/locks): Apple Silicon Macs, x86_64 and ARM64 Linux, and x64 Windows. `translated` is a
|
|
43
|
+
// Node that runs as an Intel program on an Apple Silicon Mac.
|
|
44
|
+
const NOT_SUPPORTED = 'Images and documents are not supported on this computer yet. Your text memories keep working.';
|
|
45
|
+
|
|
46
|
+
function mediaPlatformRefusal({ platform, arch, translated } = {}) {
|
|
47
|
+
if (platform === 'darwin' && arch === 'x64') {
|
|
48
|
+
if (translated) {
|
|
49
|
+
return 'This Node.js runs as an Intel program on an Apple Silicon Mac, so SLM cannot tell that your '
|
|
50
|
+
+ 'Mac can use images and documents. Run Node natively (the Apple Silicon build), then try again.';
|
|
51
|
+
}
|
|
52
|
+
return NOT_SUPPORTED;
|
|
53
|
+
}
|
|
54
|
+
if (platform === 'linux' && arch !== 'x64' && arch !== 'arm64') return NOT_SUPPORTED;
|
|
55
|
+
if (platform === 'win32' && arch !== 'x64') return NOT_SUPPORTED;
|
|
56
|
+
return '';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function currentMachine() {
|
|
60
|
+
return { platform: process.platform, arch: process.arch, translated: nodeRunsTranslated(process.platform) };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function printWhatsNew(log = console.log) {
|
|
64
|
+
log('');
|
|
65
|
+
log("What's new in 4.1.25:");
|
|
66
|
+
log(' + Images & documents: off until you turn them on (about 1.5 GB of models). Run: slm media enable');
|
|
67
|
+
log(' + Bots on the web can message each other through the mesh (remote access is still off).');
|
|
68
|
+
log(' + See what is on and what you can turn on: slm features');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Same alias order as every other entry point.
|
|
72
|
+
function resolveDataRoot(env = process.env) {
|
|
73
|
+
return env.SLM_DATA_DIR
|
|
74
|
+
|| env.SL_MEMORY_PATH
|
|
75
|
+
|| env.SLM_HOME
|
|
76
|
+
|| path.join(env.HOME || os.homedir(), '.superlocalmemory');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function mediaRequested(args, env) {
|
|
80
|
+
const viaNpm = env && ['true', '1'].includes(String(env.npm_config_media || '').toLowerCase());
|
|
81
|
+
return Boolean((args && args.media) || (env && env.SLM_ENABLE_MEDIA === '1') || viaNpm);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Merge {"media": {"requested": true, ...}} into features.json (mode 0600).
|
|
86
|
+
* Never throws; returns { ok, note }.
|
|
87
|
+
*/
|
|
88
|
+
function recordMediaRequest(slmDir, now = new Date()) {
|
|
89
|
+
const file = path.join(slmDir, FEATURES_FILE);
|
|
90
|
+
try {
|
|
91
|
+
let data = { schema: 1 };
|
|
92
|
+
if (fs.existsSync(file)) {
|
|
93
|
+
try {
|
|
94
|
+
data = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
95
|
+
} catch (_e) {
|
|
96
|
+
return { ok: false, note: 'features.json could not be read; left untouched. Run: slm media enable' };
|
|
97
|
+
}
|
|
98
|
+
if (!data || typeof data !== 'object' || Array.isArray(data)) {
|
|
99
|
+
return { ok: false, note: 'features.json has an unexpected shape; left untouched. Run: slm media enable' };
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const media = data.media && typeof data.media === 'object' ? data.media : {};
|
|
103
|
+
if (media.enabled) return { ok: true, note: 'images and documents are already on' };
|
|
104
|
+
data.media = Object.assign({}, media, {
|
|
105
|
+
requested: true,
|
|
106
|
+
requested_at: now.toISOString(),
|
|
107
|
+
choice_source: 'npm',
|
|
108
|
+
});
|
|
109
|
+
fs.mkdirSync(slmDir, { recursive: true });
|
|
110
|
+
const tmp = file + '.tmp';
|
|
111
|
+
const fd = fs.openSync(tmp, 'w', 0o600);
|
|
112
|
+
try {
|
|
113
|
+
fs.writeSync(fd, JSON.stringify(data), 0, 'utf8');
|
|
114
|
+
} finally {
|
|
115
|
+
fs.closeSync(fd);
|
|
116
|
+
}
|
|
117
|
+
fs.renameSync(tmp, file);
|
|
118
|
+
if (process.platform !== 'win32') {
|
|
119
|
+
try { fs.chmodSync(file, 0o600); } catch (_e) { /* best-effort */ }
|
|
120
|
+
}
|
|
121
|
+
return { ok: true, note: 'recorded' };
|
|
122
|
+
} catch (e) {
|
|
123
|
+
return { ok: false, note: 'could not record the request (' + e.message + '). Run: slm media enable' };
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function askYesNo(question) {
|
|
128
|
+
return new Promise((resolve) => {
|
|
129
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
130
|
+
rl.question(question, (answer) => {
|
|
131
|
+
rl.close();
|
|
132
|
+
resolve(/^\s*y(es)?\s*$/i.test(answer || ''));
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Decide and (unless dry-run) record. `interactive` allows the question,
|
|
139
|
+
* which defaults to No. Returns true when a request was recorded. On a computer
|
|
140
|
+
* with less than 16 GB of memory it says so once and neither asks nor records.
|
|
141
|
+
*/
|
|
142
|
+
async function handleMediaChoice({ args, env, slmDir, interactive, ask = askYesNo, log = console.log,
|
|
143
|
+
totalMem = os.totalmem(), machine = currentMachine() }) {
|
|
144
|
+
let wanted = mediaRequested(args, env);
|
|
145
|
+
const unsupported = mediaPlatformRefusal(machine);
|
|
146
|
+
if ((wanted || interactive) && unsupported) {
|
|
147
|
+
log('');
|
|
148
|
+
log('SLM: ' + unsupported);
|
|
149
|
+
return false; // not asked, nothing recorded: the daemon would refuse it anyway
|
|
150
|
+
}
|
|
151
|
+
const refusal = ramRefusal(totalMem, env);
|
|
152
|
+
if ((wanted || interactive) && refusal) {
|
|
153
|
+
log('');
|
|
154
|
+
log('SLM: ' + refusal);
|
|
155
|
+
return false; // not asked, nothing recorded: the daemon would refuse it anyway
|
|
156
|
+
}
|
|
157
|
+
if ((wanted || interactive) && totalMem > 0 && totalMem < MIN_RAM_BYTES) {
|
|
158
|
+
log('SLM: ' + LOW_RAM_OVERRIDE_ENV + '=1 is set; allowing images and documents on '
|
|
159
|
+
+ (totalMem / GIB).toFixed(1) + ' GB of memory (16 GB needed).');
|
|
160
|
+
}
|
|
161
|
+
if (!wanted && interactive) {
|
|
162
|
+
log('');
|
|
163
|
+
wanted = await ask('Turn on images and documents later? It downloads about 1.5 GB when SLM next starts. [y/N] ');
|
|
164
|
+
}
|
|
165
|
+
if (!wanted) return false;
|
|
166
|
+
if (args && args.dryRun) {
|
|
167
|
+
log('SLM dry-run: would record a request to turn on images and documents.');
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
const result = recordMediaRequest(slmDir);
|
|
171
|
+
if (result.ok) {
|
|
172
|
+
log('SLM: images and documents will start setting up the next time SLM starts (' + result.note + ').');
|
|
173
|
+
} else {
|
|
174
|
+
log('SLM: ' + result.note);
|
|
175
|
+
}
|
|
176
|
+
return result.ok;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* The step npm's own postinstall runs: --media / npm_config_media /
|
|
181
|
+
* SLM_ENABLE_MEDIA=1 record a request; on a real terminal (never in CI) a
|
|
182
|
+
* default-No question is asked. Never downloads, never throws.
|
|
183
|
+
*/
|
|
184
|
+
async function runMediaStep({ argv = [], env = process.env, tty, ask, log = console.log } = {}) {
|
|
185
|
+
try {
|
|
186
|
+
const isTty = tty !== undefined ? tty
|
|
187
|
+
: Boolean(process.stdout.isTTY && process.stdin.isTTY);
|
|
188
|
+
const ci = env.CI === 'true' || env.CI === '1';
|
|
189
|
+
return await handleMediaChoice({
|
|
190
|
+
args: { media: argv.includes('--media'), dryRun: argv.includes('--dry-run') },
|
|
191
|
+
env,
|
|
192
|
+
slmDir: resolveDataRoot(env),
|
|
193
|
+
interactive: isTty && !ci,
|
|
194
|
+
ask: ask || askYesNo,
|
|
195
|
+
log,
|
|
196
|
+
});
|
|
197
|
+
} catch (e) {
|
|
198
|
+
log('SLM: could not record the images and documents choice (' + e.message + '). Run: slm media enable');
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
module.exports = { mediaPlatformRefusal, ramRefusal, MIN_RAM_BYTES, runMediaStep, resolveDataRoot, printWhatsNew, mediaRequested, recordMediaRequest, handleMediaChoice, FEATURES_FILE };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether this Node.js is an Intel program run by Rosetta on an Apple Silicon Mac.
|
|
3
|
+
*
|
|
4
|
+
* Both the Python check and the pictures request need to tell that apart from a
|
|
5
|
+
* real Intel Mac: a universal2 Python started by a translated Node reports x86_64.
|
|
6
|
+
*
|
|
7
|
+
* Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
|
|
8
|
+
* Licensed under AGPL-3.0-or-later.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
const os = require('os');
|
|
14
|
+
const { spawnSync } = require('child_process');
|
|
15
|
+
|
|
16
|
+
// Only macOS has the question; any failure to ask answers false.
|
|
17
|
+
function nodeRunsTranslated(platform = os.platform(), run = spawnSync) {
|
|
18
|
+
if (platform !== 'darwin') return false;
|
|
19
|
+
try {
|
|
20
|
+
const result = run('sysctl', ['-n', 'sysctl.proc_translated'], { stdio: 'pipe', timeout: 3000, encoding: 'utf8' });
|
|
21
|
+
return Boolean(result) && !result.error && String(result.stdout || '').trim() === '1';
|
|
22
|
+
} catch (_) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
module.exports = { nodeRunsTranslated };
|
|
@@ -33,9 +33,11 @@
|
|
|
33
33
|
* --reconfigure Allow overwrite of existing config.toml.
|
|
34
34
|
* --home=<path> Override $HOME (test hook).
|
|
35
35
|
* --reply-file=<json> JSON file providing custom-knob answers.
|
|
36
|
+
* --media Record a request to turn on images and documents.
|
|
36
37
|
*
|
|
37
38
|
* Environment variables (test/CI hooks):
|
|
38
39
|
* CI=true Force non-TTY path.
|
|
40
|
+
* SLM_ENABLE_MEDIA=1 Same as --media (records a request; never downloads).
|
|
39
41
|
* SLM_INSTALL_FREE_RAM_MB=<int> Override free-RAM probe (benchmark).
|
|
40
42
|
* SLM_INSTALL_COLD_START_MS=<n> Override Python cold-start probe.
|
|
41
43
|
* SLM_INSTALL_DISK_FREE_GB=<n> Override disk-free probe.
|
|
@@ -144,6 +146,7 @@ function parseArgs(argv) {
|
|
|
144
146
|
home: null,
|
|
145
147
|
replyFile: null,
|
|
146
148
|
homeOutsideHome: false, // H-10: opt-in flag for --home outside $HOME
|
|
149
|
+
media: false, // --media: record a request to turn on images and documents
|
|
147
150
|
// v3.8.0: deployment mode — "personal" | "enterprise" (default: personal)
|
|
148
151
|
deployment: null,
|
|
149
152
|
};
|
|
@@ -151,6 +154,7 @@ function parseArgs(argv) {
|
|
|
151
154
|
if (a === '--dry-run') args.dryRun = true;
|
|
152
155
|
else if (a === '--reconfigure') args.reconfigure = true;
|
|
153
156
|
else if (a === '--home-outside-home') args.homeOutsideHome = true; // H-10
|
|
157
|
+
else if (a === '--media') args.media = true;
|
|
154
158
|
else if (a.startsWith('--profile=')) args.profile = a.slice('--profile='.length);
|
|
155
159
|
else if (a.startsWith('--home=')) args.home = a.slice('--home='.length);
|
|
156
160
|
else if (a.startsWith('--reply-file=')) args.replyFile = a.slice('--reply-file='.length);
|
|
@@ -188,6 +192,9 @@ const {
|
|
|
188
192
|
executeIdeConnections,
|
|
189
193
|
} = require('./postinstall/ide-multiselect.js');
|
|
190
194
|
|
|
195
|
+
// 4.1.25 — what's-new banner + the (never-downloading) media request
|
|
196
|
+
const { printWhatsNew, handleMediaChoice } = require('./postinstall/media-request.js');
|
|
197
|
+
|
|
191
198
|
// ------------------------------------------------------------------------
|
|
192
199
|
// TTY detection
|
|
193
200
|
// ------------------------------------------------------------------------
|
|
@@ -526,22 +533,6 @@ async function runInteractiveFlow(rl, recommendedProfile) {
|
|
|
526
533
|
// First-run checklist
|
|
527
534
|
// ------------------------------------------------------------------------
|
|
528
535
|
|
|
529
|
-
// UX-G2 — one-screen "what's new in v3.4.21" banner for upgraders so
|
|
530
|
-
// existing users see the headline before the first-run checklist. Kept
|
|
531
|
-
// under 60 LOC per Stage-8 G2 scope.
|
|
532
|
-
function printLivingBrainDelta() {
|
|
533
|
-
console.log('');
|
|
534
|
-
console.log('Current setup guarantees:');
|
|
535
|
-
console.log(' + session_init mandate hook — Claude calls ToolSearch→session_init first, every session');
|
|
536
|
-
console.log(' + External IDE integrations are installed only after explicit consent in `slm setup`');
|
|
537
|
-
console.log(' + M017 migration — ccq_consolidated_blocks gets scope column (no more silent CCQ scope drop)');
|
|
538
|
-
console.log(' + GC-safe test flags baked into pyproject.toml + Makefile (no more macOS ARM SIGSEGV)');
|
|
539
|
-
console.log('What\'s unchanged:');
|
|
540
|
-
console.log(' * Your memory.db — zero deletes, zero rewrites');
|
|
541
|
-
console.log(' * Your profile settings');
|
|
542
|
-
console.log(' * All CLI commands you already use');
|
|
543
|
-
}
|
|
544
|
-
|
|
545
536
|
function printFirstRunChecklist(config) {
|
|
546
537
|
console.log('');
|
|
547
538
|
console.log('SuperLocalMemory is configured.');
|
|
@@ -608,6 +599,8 @@ async function main() {
|
|
|
608
599
|
if (cfgExists && !args.reconfigure) {
|
|
609
600
|
console.log('SLM: existing config.toml detected at ' + cfgPath);
|
|
610
601
|
console.log('SLM: skipping installer. Use --reconfigure to change settings.');
|
|
602
|
+
printWhatsNew();
|
|
603
|
+
await handleMediaChoice({ args, env: process.env, slmDir, interactive: false });
|
|
611
604
|
return 0;
|
|
612
605
|
}
|
|
613
606
|
|
|
@@ -710,6 +703,8 @@ async function main() {
|
|
|
710
703
|
if (args.dryRun) {
|
|
711
704
|
console.log('SLM dry-run: would write profile=' + config.profile +
|
|
712
705
|
' to ' + cfgPath);
|
|
706
|
+
printWhatsNew();
|
|
707
|
+
await handleMediaChoice({ args, env: process.env, slmDir, interactive: false });
|
|
713
708
|
printFirstRunChecklist(config);
|
|
714
709
|
return 0;
|
|
715
710
|
}
|
|
@@ -764,8 +759,9 @@ async function main() {
|
|
|
764
759
|
// v3.8.0: Execute IDE connections after config write so state is consistent.
|
|
765
760
|
if (config._pendingIdes) executeIdeConnections(config._pendingIdes, slmDir);
|
|
766
761
|
|
|
767
|
-
//
|
|
768
|
-
|
|
762
|
+
// Show what shipped, then the (default-No) question about images and documents.
|
|
763
|
+
printWhatsNew();
|
|
764
|
+
await handleMediaChoice({ args, env: process.env, slmDir, interactive: !nonInteractive });
|
|
769
765
|
|
|
770
766
|
printFirstRunChecklist(config);
|
|
771
767
|
return 0;
|
|
@@ -800,7 +796,7 @@ module.exports = {
|
|
|
800
796
|
LLM_MODEL_CHOICES,
|
|
801
797
|
PROFILES,
|
|
802
798
|
CUSTOM_KNOB_ENUMS, // UX-M3
|
|
803
|
-
|
|
799
|
+
printWhatsNew, // shared with scripts/postinstall.js (exposed for the test harness)
|
|
804
800
|
// v3.8.0 — deployment
|
|
805
801
|
DEPLOYMENT_PRESETS,
|
|
806
802
|
DEFAULT_DEPLOYMENT_MODE,
|
package/scripts/postinstall.js
CHANGED
|
@@ -16,6 +16,9 @@ const { spawnSync } = require('child_process');
|
|
|
16
16
|
const fs = require('fs');
|
|
17
17
|
const path = require('path');
|
|
18
18
|
const os = require('os');
|
|
19
|
+
const { printWhatsNew, runMediaStep } = require('./postinstall/media-request.js');
|
|
20
|
+
const { nodeRunsTranslated } = require('./postinstall/node-arch.js');
|
|
21
|
+
const { runUpgradeStep } = require('./postinstall/engine-upgrade.js');
|
|
19
22
|
|
|
20
23
|
const MIN_PYTHON = Object.freeze([3, 12]);
|
|
21
24
|
const MAX_PYTHON_EXCLUSIVE = Object.freeze([3, 15]);
|
|
@@ -242,13 +245,82 @@ function validateRuntimeLocation(venvRoot) {
|
|
|
242
245
|
}
|
|
243
246
|
}
|
|
244
247
|
|
|
248
|
+
function pythonGuidance(platform = os.platform()) {
|
|
249
|
+
const lines = ['', 'SuperLocalMemory requires Python 3.12, 3.13, or 3.14.'];
|
|
250
|
+
if (platform === 'darwin') {
|
|
251
|
+
lines.push('Install it with Homebrew: brew install python@3.13');
|
|
252
|
+
lines.push('or the installer from https://www.python.org/downloads/macos/');
|
|
253
|
+
} else if (platform === 'win32') {
|
|
254
|
+
lines.push('Install it with: winget install Python.Python.3.13');
|
|
255
|
+
lines.push('or from https://www.python.org/downloads/windows/ (the py launcher is found automatically).');
|
|
256
|
+
} else if (platform === 'linux') {
|
|
257
|
+
lines.push('Debian 13 / Ubuntu 24.04 and later: sudo apt install python3 python3-venv');
|
|
258
|
+
lines.push('Ubuntu 22.04: sudo add-apt-repository ppa:deadsnakes/ppa && sudo apt install python3.12 python3.12-venv');
|
|
259
|
+
lines.push('Fedora / RHEL 9: sudo dnf install python3.12');
|
|
260
|
+
} else {
|
|
261
|
+
lines.push('Install it from https://www.python.org/downloads/');
|
|
262
|
+
}
|
|
263
|
+
lines.push('Then finish the install: npm rebuild -g superlocalmemory');
|
|
264
|
+
lines.push('(or set SLM_PYTHON to the interpreter if it is somewhere unusual).');
|
|
265
|
+
lines.push('The npm installer creates a private virtual environment;');
|
|
266
|
+
lines.push('it never installs packages into your system Python.');
|
|
267
|
+
return lines;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Machines SuperLocalMemory cannot run on: an Intel (x86_64) Python on macOS (the
|
|
271
|
+
// pinned security library, cryptography 50, has no Intel Mac build), a 32-bit Python
|
|
272
|
+
// on Windows or Linux, and Windows on ARM. pip would fail deep in dependency
|
|
273
|
+
// resolution instead. An Intel answer on a Mac can be false: a universal2 Python
|
|
274
|
+
// started by a Node that runs translated (Rosetta) reports x86_64 although the Mac
|
|
275
|
+
// is Apple Silicon, so `translated` (see nodeRunsTranslated) changes the advice.
|
|
276
|
+
function unsupportedMachine(platform, machine, is64, translated = false) {
|
|
277
|
+
const m = String(machine || '').toLowerCase();
|
|
278
|
+
if (platform === 'darwin' && m === 'x86_64') {
|
|
279
|
+
if (translated === true) {
|
|
280
|
+
return [
|
|
281
|
+
'This Node.js is running as an Intel program on an Apple Silicon Mac (translated by Rosetta),',
|
|
282
|
+
'so the Python it starts reports Intel too. SuperLocalMemory needs everything to run natively.',
|
|
283
|
+
'Install the Apple Silicon (arm64) Node.js (nodejs.org installer, or Homebrew in /opt/homebrew),',
|
|
284
|
+
'open a new terminal, and run Node natively, then: npm rebuild -g superlocalmemory',
|
|
285
|
+
].join('\n');
|
|
286
|
+
}
|
|
287
|
+
return [
|
|
288
|
+
'This Python is an Intel (x86_64) build, and SuperLocalMemory needs an Apple Silicon (arm64) one.',
|
|
289
|
+
'On an Apple Silicon Mac, install the arm64 Python (Homebrew in /opt/homebrew: brew install python@3.13,',
|
|
290
|
+
'or the python.org installer run natively, not under Rosetta), then: npm rebuild -g superlocalmemory',
|
|
291
|
+
'Intel Macs are not supported: the security library SuperLocalMemory pins has no Intel Mac build.',
|
|
292
|
+
].join('\n');
|
|
293
|
+
}
|
|
294
|
+
if (platform === 'win32' && m === 'arm64') {
|
|
295
|
+
return 'Windows on ARM is not supported yet: the security library SuperLocalMemory pins has no '
|
|
296
|
+
+ 'Windows ARM build. Use a 64-bit Intel or AMD Windows computer, or WSL with an x86_64 Linux.';
|
|
297
|
+
}
|
|
298
|
+
if (platform === 'win32' && is64 === false) {
|
|
299
|
+
return 'This Python is 32-bit. SuperLocalMemory needs 64-bit Python 3.12-3.14 on Windows '
|
|
300
|
+
+ '(winget install Python.Python.3.13), then: npm rebuild -g superlocalmemory';
|
|
301
|
+
}
|
|
302
|
+
if (platform === 'linux' && (is64 === false || ['i386', 'i486', 'i586', 'i686', 'armv6l', 'armv7l'].includes(m))) {
|
|
303
|
+
return 'This computer or this Python is 32-bit. SuperLocalMemory needs a 64-bit Linux '
|
|
304
|
+
+ '(x86_64 or aarch64) with 64-bit Python 3.12-3.14, then: npm rebuild -g superlocalmemory';
|
|
305
|
+
}
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function pythonMachine(python) {
|
|
310
|
+
try {
|
|
311
|
+
const result = spawnSync(python.command, [...python.prefixArgs, '-c',
|
|
312
|
+
'import platform, sys; print(sys.platform, platform.machine(), sys.maxsize > 2**32)'], {
|
|
313
|
+
stdio: 'pipe', timeout: 5000, env: process.env,
|
|
314
|
+
});
|
|
315
|
+
const [platform, machine, wide] = String(result.stdout || '').trim().split(/\s+/);
|
|
316
|
+
return { platform: platform || '', machine: machine || '', is64: wide === 'True' ? true : wide === 'False' ? false : null };
|
|
317
|
+
} catch (_) {
|
|
318
|
+
return { platform: '', machine: '', is64: null };
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
245
322
|
function printPythonGuidance() {
|
|
246
|
-
console.error(
|
|
247
|
-
console.error('SuperLocalMemory requires Python 3.12, 3.13, or 3.14.');
|
|
248
|
-
console.error('Install Python from https://www.python.org/downloads/ and rerun:');
|
|
249
|
-
console.error(' npm rebuild superlocalmemory');
|
|
250
|
-
console.error('The npm installer will create a private virtual environment;');
|
|
251
|
-
console.error('it will not install packages into your system Python.');
|
|
323
|
+
for (const line of pythonGuidance()) console.error(line);
|
|
252
324
|
}
|
|
253
325
|
|
|
254
326
|
function failureDetail(result) {
|
|
@@ -277,12 +349,19 @@ function main(argv = process.argv.slice(2)) {
|
|
|
277
349
|
printPythonGuidance();
|
|
278
350
|
return 1;
|
|
279
351
|
}
|
|
352
|
+
const machine = pythonMachine(python);
|
|
353
|
+
const refusal = unsupportedMachine(machine.platform, machine.machine, machine.is64, nodeRunsTranslated());
|
|
354
|
+
if (refusal) {
|
|
355
|
+
console.error('');
|
|
356
|
+
console.error(refusal);
|
|
357
|
+
return 1;
|
|
358
|
+
}
|
|
280
359
|
|
|
281
360
|
const locationCheck = validateRuntimeLocation(venvRoot);
|
|
282
361
|
if (!locationCheck.ok) {
|
|
283
362
|
console.error(`SuperLocalMemory: refusing unsafe runtime location: ${locationCheck.error}.`);
|
|
284
363
|
console.error('Move that path aside manually, verify it contains no needed files, then run:');
|
|
285
|
-
console.error(' npm rebuild superlocalmemory');
|
|
364
|
+
console.error(' npm rebuild -g superlocalmemory');
|
|
286
365
|
return 1;
|
|
287
366
|
}
|
|
288
367
|
|
|
@@ -311,7 +390,7 @@ function main(argv = process.argv.slice(2)) {
|
|
|
311
390
|
} else {
|
|
312
391
|
console.error('Repair the selected Python installation so the stdlib venv module is available, then run:');
|
|
313
392
|
}
|
|
314
|
-
console.error(' npm rebuild superlocalmemory');
|
|
393
|
+
console.error(' npm rebuild -g superlocalmemory');
|
|
315
394
|
return 1;
|
|
316
395
|
}
|
|
317
396
|
|
|
@@ -361,10 +440,9 @@ function main(argv = process.argv.slice(2)) {
|
|
|
361
440
|
if (installPackage.status !== 0) {
|
|
362
441
|
console.error(`SuperLocalMemory: private-runtime installation failed (${failureDetail(installPackage)}).`);
|
|
363
442
|
console.error(`Tried Python source: ${packageSource}`);
|
|
364
|
-
console.error('This install needs network access to PyPI
|
|
365
|
-
console.error(' npm rebuild superlocalmemory');
|
|
443
|
+
console.error('This install needs network access to PyPI (or set SLM_LOCAL_WHEEL=/path/to/superlocalmemory-<version>-py3-none-any.whl for air-gapped installs).');
|
|
366
444
|
console.error('Check network access, available disk space, and Python build prerequisites, then run:');
|
|
367
|
-
console.error(' npm rebuild superlocalmemory');
|
|
445
|
+
console.error(' npm rebuild -g superlocalmemory');
|
|
368
446
|
console.error('No system Python packages or SLM durable data were modified.');
|
|
369
447
|
return 1;
|
|
370
448
|
}
|
|
@@ -391,7 +469,7 @@ function main(argv = process.argv.slice(2)) {
|
|
|
391
469
|
console.error(
|
|
392
470
|
`SuperLocalMemory: runtime identity check failed (npm=${packageVersion}, python=${installedVersion || 'unavailable'}).`,
|
|
393
471
|
);
|
|
394
|
-
console.error('Run `npm rebuild superlocalmemory` to repair the package-owned runtime.');
|
|
472
|
+
console.error('Run `npm rebuild -g superlocalmemory` to repair the package-owned runtime.');
|
|
395
473
|
return 1;
|
|
396
474
|
}
|
|
397
475
|
|
|
@@ -416,12 +494,16 @@ function main(argv = process.argv.slice(2)) {
|
|
|
416
494
|
console.log(' slm upgrade-hosts');
|
|
417
495
|
console.log('');
|
|
418
496
|
console.log(' Prefer to tune performance profiles? Use: slm reconfigure');
|
|
497
|
+
printWhatsNew();
|
|
419
498
|
console.log('');
|
|
420
499
|
return 0;
|
|
421
500
|
}
|
|
422
501
|
|
|
423
502
|
if (require.main === module) {
|
|
424
|
-
|
|
503
|
+
const code = main();
|
|
504
|
+
const done = () => process.exit(code);
|
|
505
|
+
if (code !== 0) done();
|
|
506
|
+
else runMediaStep({ argv: process.argv.slice(2) }).then(() => runUpgradeStep()).then(done, done);
|
|
425
507
|
}
|
|
426
508
|
|
|
427
509
|
module.exports = {
|
|
@@ -430,10 +512,14 @@ module.exports = {
|
|
|
430
512
|
hasNvidiaGpu,
|
|
431
513
|
isSupportedPython,
|
|
432
514
|
main,
|
|
515
|
+
runMediaStep,
|
|
433
516
|
parsePythonVersion,
|
|
434
517
|
probePython,
|
|
435
518
|
pypiSpecifier,
|
|
436
519
|
pythonCandidates,
|
|
520
|
+
pythonGuidance,
|
|
521
|
+
unsupportedMachine,
|
|
522
|
+
nodeRunsTranslated,
|
|
437
523
|
runtimePythonPath,
|
|
438
524
|
shouldForceCpuTorch,
|
|
439
525
|
TORCH_CPU_INDEX_URL,
|
package/scripts/preuninstall.js
CHANGED
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
* SuperLocalMemory NPM pre-uninstall notice.
|
|
4
4
|
*
|
|
5
5
|
* npm owns and removes the package directory, including its private Python
|
|
6
|
-
* runtime. Durable SLM data has separate ownership and is never
|
|
7
|
-
*
|
|
6
|
+
* runtime. Durable SLM data has separate ownership and is never modified or
|
|
7
|
+
* deleted by an application-code uninstall. The one thing worth saying: the
|
|
8
|
+
* images-and-documents models (about 1.5 GB) live in the data folder, so this
|
|
9
|
+
* names where they are, how big they are, and the command that removes them.
|
|
8
10
|
*
|
|
9
11
|
* Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
|
|
10
12
|
* Licensed under AGPL-3.0-or-later.
|
|
@@ -12,5 +14,55 @@
|
|
|
12
14
|
|
|
13
15
|
'use strict';
|
|
14
16
|
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
const os = require('os');
|
|
19
|
+
const path = require('path');
|
|
20
|
+
|
|
21
|
+
function dataRoot(env = process.env) {
|
|
22
|
+
return env.SLM_DATA_DIR
|
|
23
|
+
|| env.SL_MEMORY_PATH
|
|
24
|
+
|| env.SLM_HOME
|
|
25
|
+
|| path.join(os.homedir(), '.superlocalmemory');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Total bytes under dir; read-only, bounded, never follows links, never throws.
|
|
29
|
+
function folderBytes(dir, limit = 200000) {
|
|
30
|
+
let total = 0;
|
|
31
|
+
let seen = 0;
|
|
32
|
+
const stack = [dir];
|
|
33
|
+
while (stack.length && seen < limit) {
|
|
34
|
+
const current = stack.pop();
|
|
35
|
+
let entries = [];
|
|
36
|
+
try { entries = fs.readdirSync(current, { withFileTypes: true }); } catch (_e) { continue; }
|
|
37
|
+
for (const entry of entries) {
|
|
38
|
+
seen += 1;
|
|
39
|
+
const full = path.join(current, entry.name);
|
|
40
|
+
try {
|
|
41
|
+
if (entry.isSymbolicLink()) continue;
|
|
42
|
+
if (entry.isDirectory()) stack.push(full);
|
|
43
|
+
else total += fs.lstatSync(full).size;
|
|
44
|
+
} catch (_e) { /* skip unreadable entries */ }
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return total;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function humanSize(bytes) {
|
|
51
|
+
if (bytes >= 1024 ** 3) return (bytes / 1024 ** 3).toFixed(1) + ' GB';
|
|
52
|
+
return Math.max(1, Math.round(bytes / 1024 ** 2)) + ' MB';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function mediaEnvNotice(env = process.env, log = console.log) {
|
|
56
|
+
try {
|
|
57
|
+
const mediaDir = path.join(dataRoot(env), 'runtimes', 'media');
|
|
58
|
+
if (!fs.existsSync(mediaDir)) return;
|
|
59
|
+
log('Images & documents models are kept at ' + mediaDir + ' (' + humanSize(folderBytes(mediaDir)) + ').');
|
|
60
|
+
log('To remove them, run: slm media disable --remove-files');
|
|
61
|
+
} catch (_e) { /* a notice must never fail an uninstall */ }
|
|
62
|
+
}
|
|
63
|
+
|
|
15
64
|
console.log('SuperLocalMemory application code is being removed.');
|
|
16
|
-
console.log('Memory data is preserved. No database, profile, backup, or configuration was
|
|
65
|
+
console.log('Memory data is preserved. No database, profile, backup, or configuration was changed.');
|
|
66
|
+
mediaEnvNotice();
|
|
67
|
+
|
|
68
|
+
module.exports = { mediaEnvNotice, folderBytes, dataRoot };
|