blun-king-cli 9.1.336 → 9.1.337
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.
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const fs = require('node:fs');
|
|
4
4
|
const os = require('node:os');
|
|
5
5
|
const path = require('node:path');
|
|
6
|
-
const { readProfilePersona, resolveSoulFile } = require('./profile-identity-resolution.cjs');
|
|
6
|
+
const { readProfilePersona, resolveLegacySoulFile, resolveSoulFile } = require('./profile-identity-resolution.cjs');
|
|
7
7
|
|
|
8
8
|
const SAFE_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u;
|
|
9
9
|
const MAX_PERSONA_BYTES = 64 * 1024;
|
|
@@ -83,6 +83,43 @@ function createTextOnce(root, target, value) {
|
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
function generatedSoulText(displayName) {
|
|
87
|
+
return `# ${displayName}\n\nIch bin ${displayName}, der persoenliche BLUN-Agent dieses Profils. Meine eigene Stimme, Vorlieben und gewachsene gemeinsame Geschichte werden hier behutsam weiterentwickelt. Bestaetigte Beziehungen gehoeren in den Beziehungsgraphen; Auftraege, Rechte, Secrets und Incident-Logs gehoeren nicht in meine Seele.\n`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function importLegacySoulOverGeneratedProfile(env, profileHome, profileSoulPath, displayName) {
|
|
91
|
+
let current;
|
|
92
|
+
try {
|
|
93
|
+
const stat = fs.lstatSync(profileSoulPath);
|
|
94
|
+
if (!stat.isFile() || stat.isSymbolicLink() || stat.size > MAX_PERSONA_BYTES) return false;
|
|
95
|
+
current = fs.readFileSync(profileSoulPath, 'utf8');
|
|
96
|
+
} catch {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
if (current.trim() !== generatedSoulText(displayName).trim()) return false;
|
|
100
|
+
const legacy = resolveLegacySoulFile(env);
|
|
101
|
+
if (!legacy.text || path.resolve(legacy.path) === path.resolve(profileSoulPath)) return false;
|
|
102
|
+
|
|
103
|
+
const backupPath = `${profileSoulPath}.pre-legacy-import`;
|
|
104
|
+
createTextOnce(profileHome, backupPath, current);
|
|
105
|
+
if (!fs.existsSync(backupPath) || fs.readFileSync(backupPath, 'utf8') !== current) return false;
|
|
106
|
+
|
|
107
|
+
const temporaryPath = path.join(profileHome, `.SOUL.md.import-${process.pid}-${Date.now()}`);
|
|
108
|
+
let handle;
|
|
109
|
+
try {
|
|
110
|
+
handle = fs.openSync(temporaryPath, 'wx', 0o600);
|
|
111
|
+
fs.writeFileSync(handle, `${legacy.text}\n`, 'utf8');
|
|
112
|
+
fs.fsyncSync(handle);
|
|
113
|
+
fs.closeSync(handle);
|
|
114
|
+
handle = undefined;
|
|
115
|
+
fs.renameSync(temporaryPath, profileSoulPath);
|
|
116
|
+
return true;
|
|
117
|
+
} finally {
|
|
118
|
+
if (handle !== undefined) fs.closeSync(handle);
|
|
119
|
+
try { fs.unlinkSync(temporaryPath); } catch {}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
86
123
|
function ensurePersonalityWorkspace(env = process.env) {
|
|
87
124
|
const home = resolvedHome(env);
|
|
88
125
|
const profileHome = path.resolve(String(env.BLUN_HOME ?? '').trim() || home);
|
|
@@ -134,15 +171,13 @@ function ensurePersonalityWorkspace(env = process.env) {
|
|
|
134
171
|
);
|
|
135
172
|
const resolvedSoul = resolveSoulFile(env);
|
|
136
173
|
const profileSoulPath = path.join(profileHome, 'SOUL.md');
|
|
174
|
+
let soulMigrated = false;
|
|
137
175
|
if (resolvedSoul.text && path.resolve(resolvedSoul.path) !== path.resolve(profileSoulPath)) {
|
|
138
176
|
createTextOnce(profileHome, profileSoulPath, `${resolvedSoul.text}\n`);
|
|
139
177
|
} else if (!resolvedSoul.text) {
|
|
140
|
-
createTextOnce(
|
|
141
|
-
profileHome,
|
|
142
|
-
profileSoulPath,
|
|
143
|
-
`# ${displayName}\n\nIch bin ${displayName}, der persoenliche BLUN-Agent dieses Profils. Meine eigene Stimme, Vorlieben und gewachsene gemeinsame Geschichte werden hier behutsam weiterentwickelt. Bestaetigte Beziehungen gehoeren in den Beziehungsgraphen; Auftraege, Rechte, Secrets und Incident-Logs gehoeren nicht in meine Seele.\n`,
|
|
144
|
-
);
|
|
178
|
+
createTextOnce(profileHome, profileSoulPath, generatedSoulText(displayName));
|
|
145
179
|
}
|
|
180
|
+
soulMigrated = importLegacySoulOverGeneratedProfile(env, profileHome, profileSoulPath, displayName);
|
|
146
181
|
for (const relative of [
|
|
147
182
|
['agents', agentId, 'relationships'],
|
|
148
183
|
['agents', agentId, 'journal', 'candidates'],
|
|
@@ -156,7 +191,7 @@ function ensurePersonalityWorkspace(env = process.env) {
|
|
|
156
191
|
fs.mkdirSync(target, { recursive: true });
|
|
157
192
|
if (!isInside(root, fs.realpathSync(target))) return { ready: false, reason: 'identity_path_invalid' };
|
|
158
193
|
}
|
|
159
|
-
return { ready: true, root, tenant_id: tenantId, agent_id: agentId };
|
|
194
|
+
return { ready: true, root, tenant_id: tenantId, agent_id: agentId, soul_migrated: soulMigrated };
|
|
160
195
|
}
|
|
161
196
|
|
|
162
197
|
module.exports = { ensurePersonalityWorkspace };
|
|
@@ -77,6 +77,30 @@ function legacySoulNames(env, persona) {
|
|
|
77
77
|
return names;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
function legacySoulCandidates(env = process.env, options = {}) {
|
|
81
|
+
const homeDir = options.homeDir || os.homedir();
|
|
82
|
+
const profileHome = String(env.BLUN_HOME || '').trim();
|
|
83
|
+
const sharedHome = String(env.BLUN_SHARED_HOME || '').trim();
|
|
84
|
+
const persona = options.persona || readProfilePersona(env, options).persona;
|
|
85
|
+
const roots = uniquePaths([
|
|
86
|
+
sharedHome,
|
|
87
|
+
homeDir,
|
|
88
|
+
profileHome && path.dirname(profileHome),
|
|
89
|
+
]);
|
|
90
|
+
return legacySoulNames(env, persona)
|
|
91
|
+
.flatMap((filename) => roots.map((root) => path.join(root, filename)));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function resolveLegacySoulFile(env = process.env, options = {}) {
|
|
95
|
+
const fsImpl = options.fsImpl || fs;
|
|
96
|
+
const candidates = uniquePaths(legacySoulCandidates(env, options));
|
|
97
|
+
for (const target of candidates) {
|
|
98
|
+
const text = readSafeText(target, fsImpl);
|
|
99
|
+
if (text) return { path: target, text };
|
|
100
|
+
}
|
|
101
|
+
return { path: candidates[0], text: '' };
|
|
102
|
+
}
|
|
103
|
+
|
|
80
104
|
function resolveSoulFile(env = process.env, options = {}) {
|
|
81
105
|
const fsImpl = options.fsImpl || fs;
|
|
82
106
|
const explicit = String(env.BLUN_SOUL_PATH || '').trim();
|
|
@@ -90,13 +114,7 @@ function resolveSoulFile(env = process.env, options = {}) {
|
|
|
90
114
|
const profileName = String(env.BLUN_PROFILE || '').trim().toLowerCase();
|
|
91
115
|
const isNamedProfile = profileName && profileName !== 'default';
|
|
92
116
|
const persona = readProfilePersona(env, options).persona;
|
|
93
|
-
const
|
|
94
|
-
sharedHome,
|
|
95
|
-
homeDir,
|
|
96
|
-
profileHome && path.dirname(profileHome),
|
|
97
|
-
]);
|
|
98
|
-
const legacyCandidates = legacySoulNames(env, persona)
|
|
99
|
-
.flatMap((filename) => legacyRoots.map((root) => path.join(root, filename)));
|
|
117
|
+
const legacyCandidates = legacySoulCandidates(env, { ...options, persona });
|
|
100
118
|
const candidates = uniquePaths([
|
|
101
119
|
profileHome && path.join(profileHome, 'SOUL.md'),
|
|
102
120
|
...legacyCandidates,
|
|
@@ -113,5 +131,6 @@ module.exports = {
|
|
|
113
131
|
MAX_IDENTITY_FILE_BYTES,
|
|
114
132
|
identityFileCandidates,
|
|
115
133
|
readProfilePersona,
|
|
134
|
+
resolveLegacySoulFile,
|
|
116
135
|
resolveSoulFile,
|
|
117
136
|
};
|