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 legacyRoots = uniquePaths([
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
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.336",
3
+ "version": "9.1.337",
4
4
  "description": "BLUN CLI - your own AI agent with a Telegram channel. Get it done. With BLUN.",
5
5
  "license": "MIT",
6
6
  "bin": {