blun-king-cli 9.1.60 → 9.1.62
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/LIESMICH.txt +12 -3
- package/README.md +10 -1
- package/bin/core-bootstrap.js +3 -6
- package/bin/launcher-runtime.js +135 -12
- package/bin/profile-runtime.cjs +145 -9
- package/bin/running-update.cjs +389 -0
- package/bin/update-notice.js +24 -0
- package/blun.mjs +93 -23
- package/package.json +1 -1
- package/telegram-plugin/commands/access.md +10 -2
- package/telegram-plugin/dist/bridge.mjs +505 -63776
- package/telegram-plugin/dist/mcp-server.mjs +8976 -72843
- package/telegram-plugin/dist/noise.mjs +63473 -2
package/LIESMICH.txt
CHANGED
|
@@ -9,7 +9,7 @@ Installation
|
|
|
9
9
|
------------
|
|
10
10
|
Die geprüfte Version exakt global installieren:
|
|
11
11
|
|
|
12
|
-
npm install -g blun-king-cli@9.1.
|
|
12
|
+
npm install -g blun-king-cli@9.1.61
|
|
13
13
|
|
|
14
14
|
Start
|
|
15
15
|
-----
|
|
@@ -145,5 +145,14 @@ Telegram einrichten
|
|
|
145
145
|
|
|
146
146
|
~/.blun/channels/telegram/.env
|
|
147
147
|
|
|
148
|
-
3. `king` starten und
|
|
149
|
-
|
|
148
|
+
3. `king` starten und in der Konsole eingeben:
|
|
149
|
+
|
|
150
|
+
/telegram:access connect
|
|
151
|
+
|
|
152
|
+
4. Den dort erzeugten Einmalcode im gewünschten privaten Chat oder in der
|
|
153
|
+
gewünschten Gruppe senden:
|
|
154
|
+
|
|
155
|
+
/connect CODE
|
|
156
|
+
|
|
157
|
+
Chat-ID und Absender-ID werden intern gespeichert und müssen nie eingegeben
|
|
158
|
+
werden. Der Code verfällt nach zehn Minuten und funktioniert nur einmal.
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ Voraussetzung ist Node.js 24.15 oder neuer. Die geprüfte Version wird exakt
|
|
|
9
9
|
installiert:
|
|
10
10
|
|
|
11
11
|
```powershell
|
|
12
|
-
npm install -g blun-king-cli@9.1.
|
|
12
|
+
npm install -g blun-king-cli@9.1.61
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
## Reproduzierbares Staging und Packen
|
|
@@ -35,6 +35,11 @@ an. Version, Konto, Modell und Befehle sind ansonsten identisch. Der Unterschied
|
|
|
35
35
|
gilt nur für den laufenden Prozess; die gespeicherte
|
|
36
36
|
Plugin-Konfiguration wird nicht umgeschrieben.
|
|
37
37
|
|
|
38
|
+
Telegram-Chats werden ohne manuell eingegebene IDs verbunden. In der Konsole
|
|
39
|
+
erzeugt `/telegram:access connect` einen einmaligen Code. `/connect CODE` im
|
|
40
|
+
gewünschten privaten Chat oder in einer Gruppe speichert Chat und Absender
|
|
41
|
+
intern; der Code verfällt nach zehn Minuten und ist nur einmal verwendbar.
|
|
42
|
+
|
|
38
43
|
## Zuverlässiger King-Start
|
|
39
44
|
|
|
40
45
|
Der Windows-Hilfsprozess für private Pfade übernimmt `TEMP` und `TMP` aus der
|
|
@@ -106,6 +111,10 @@ besten sicheren lokalen nächsten Schritt um. Der sichtbare Aufgabenplan und all
|
|
|
106
111
|
Freigabegrenzen von `/idea` gelten weiterhin. Der Befehl funktioniert auch über
|
|
107
112
|
Telegram.
|
|
108
113
|
|
|
114
|
+
Der sichtbare Verlauf behält standardmäßig die vollständige laufende und
|
|
115
|
+
wiederhergestellte Sitzung. Das Mausrad kann den Verlauf direkt beim ersten Zug
|
|
116
|
+
öffnen; Editor und Fußzeile bleiben dabei fest sichtbar.
|
|
117
|
+
|
|
109
118
|
Im automatisch angebundenen Telegram-Kanal werden ausschließlich `/loop`,
|
|
110
119
|
`/goal`, `/idea`, `/chancenradar` und `/befehle` als Befehle ausgeführt. Ist der Agent
|
|
111
120
|
beschäftigt, bleiben sie in der Warteschlange. Andere Slash-Eingaben werden aus
|
package/bin/core-bootstrap.js
CHANGED
|
@@ -5,6 +5,7 @@ const path = require('node:path');
|
|
|
5
5
|
const { pathToFileURL } = require('node:url');
|
|
6
6
|
|
|
7
7
|
const { acquireSharedRuntimeLease } = require('./update-lease');
|
|
8
|
+
const { RUNTIME_READY_MESSAGE } = require('./running-update.cjs');
|
|
8
9
|
|
|
9
10
|
const CORE_LOADED_MESSAGE = 'blun-core-bootstrap-loaded';
|
|
10
11
|
|
|
@@ -31,11 +32,7 @@ async function runCoreBootstrap() {
|
|
|
31
32
|
|
|
32
33
|
if (process.connected) {
|
|
33
34
|
try {
|
|
34
|
-
process.send({ type: CORE_LOADED_MESSAGE }
|
|
35
|
-
try {
|
|
36
|
-
process.disconnect();
|
|
37
|
-
} catch {}
|
|
38
|
-
});
|
|
35
|
+
process.send({ type: CORE_LOADED_MESSAGE });
|
|
39
36
|
} catch {}
|
|
40
37
|
}
|
|
41
38
|
}
|
|
@@ -49,4 +46,4 @@ if (require.main === module) {
|
|
|
49
46
|
});
|
|
50
47
|
}
|
|
51
48
|
|
|
52
|
-
module.exports = { CORE_LOADED_MESSAGE, runCoreBootstrap };
|
|
49
|
+
module.exports = { CORE_LOADED_MESSAGE, RUNTIME_READY_MESSAGE, runCoreBootstrap };
|
package/bin/launcher-runtime.js
CHANGED
|
@@ -21,11 +21,21 @@ const {
|
|
|
21
21
|
seedStandardDesignSkill,
|
|
22
22
|
seedStandardTools,
|
|
23
23
|
} = require('./standard-tools-bootstrap');
|
|
24
|
-
const { CORE_LOADED_MESSAGE } = require('./core-bootstrap');
|
|
24
|
+
const { CORE_LOADED_MESSAGE, RUNTIME_READY_MESSAGE } = require('./core-bootstrap');
|
|
25
25
|
const { prepareManagedNodeRuntime } = require('./node-runtime');
|
|
26
26
|
const { repairConfiguredNativeModules } = require('./native-module-repair');
|
|
27
27
|
const { acquireSharedRuntimeLease, tryAcquireUpdateLease } = require('./update-lease');
|
|
28
|
-
const { runExplicitUpdate, runUpdateNotice } = require('./update-notice');
|
|
28
|
+
const { compareSemver, runExplicitUpdate, runUpdateNotice } = require('./update-notice');
|
|
29
|
+
const {
|
|
30
|
+
RUNNING_UPDATE_HANDOFF_EXIT_CODE,
|
|
31
|
+
RUNNING_UPDATE_HANDOFF_MESSAGE,
|
|
32
|
+
RUNNING_UPDATE_PREPARED_MESSAGE,
|
|
33
|
+
activateRuntime,
|
|
34
|
+
handoffRuntime,
|
|
35
|
+
prepareRunningUpdate,
|
|
36
|
+
readActiveRuntime,
|
|
37
|
+
resumeArgsForHandoff,
|
|
38
|
+
} = require('./running-update.cjs');
|
|
29
39
|
const {
|
|
30
40
|
ensurePrivateDirectory,
|
|
31
41
|
securePrivateFile,
|
|
@@ -42,6 +52,7 @@ const RAW_ARGS = process.argv.slice(2);
|
|
|
42
52
|
const PROFILE = parseProfileLaunchArgs(RAW_ARGS, launcherModeFromArgv(process.argv));
|
|
43
53
|
const ARGS = PROFILE.args;
|
|
44
54
|
const CORE_LOAD_TIMEOUT_MS = 30_000;
|
|
55
|
+
const RUNTIME_READY_TIMEOUT_MS = 60_000;
|
|
45
56
|
|
|
46
57
|
function normalizeWindowsPath(value) {
|
|
47
58
|
return path.win32.normalize(value).replace(/\\+$/u, '').toLowerCase();
|
|
@@ -130,10 +141,11 @@ function shouldDetachProtectedCore(options = {}) {
|
|
|
130
141
|
return platform !== 'win32' || stdinIsTTY !== true;
|
|
131
142
|
}
|
|
132
143
|
|
|
133
|
-
function spawnProtectedCore(args, env, cwd) {
|
|
144
|
+
function spawnProtectedCore(args, env, cwd, options = {}) {
|
|
145
|
+
const packageRoot = path.resolve(options.packageRoot || PKG);
|
|
134
146
|
const child = spawn(
|
|
135
147
|
process.execPath,
|
|
136
|
-
[path.join(
|
|
148
|
+
[path.join(packageRoot, 'bin', 'core-bootstrap.js'), ...args],
|
|
137
149
|
{
|
|
138
150
|
cwd,
|
|
139
151
|
detached: shouldDetachProtectedCore(),
|
|
@@ -143,8 +155,10 @@ function spawnProtectedCore(args, env, cwd) {
|
|
|
143
155
|
},
|
|
144
156
|
);
|
|
145
157
|
let loadedSettled = false;
|
|
158
|
+
let readySettled = false;
|
|
146
159
|
let completedSettled = false;
|
|
147
160
|
let resolveLoaded;
|
|
161
|
+
let resolveReady;
|
|
148
162
|
let resolveCompleted;
|
|
149
163
|
const loadTimer = setTimeout(() => {
|
|
150
164
|
try {
|
|
@@ -152,6 +166,7 @@ function spawnProtectedCore(args, env, cwd) {
|
|
|
152
166
|
} catch {}
|
|
153
167
|
}, CORE_LOAD_TIMEOUT_MS);
|
|
154
168
|
const loaded = new Promise((resolve) => { resolveLoaded = resolve; });
|
|
169
|
+
const ready = new Promise((resolve) => { resolveReady = resolve; });
|
|
155
170
|
const completed = new Promise((resolve) => { resolveCompleted = resolve; });
|
|
156
171
|
const settleLoaded = (value) => {
|
|
157
172
|
if (loadedSettled) return;
|
|
@@ -159,11 +174,17 @@ function spawnProtectedCore(args, env, cwd) {
|
|
|
159
174
|
clearTimeout(loadTimer);
|
|
160
175
|
resolveLoaded(value);
|
|
161
176
|
};
|
|
177
|
+
const settleReady = (value) => {
|
|
178
|
+
if (readySettled) return;
|
|
179
|
+
readySettled = true;
|
|
180
|
+
resolveReady(value);
|
|
181
|
+
};
|
|
162
182
|
const settleCompleted = (result) => {
|
|
163
183
|
if (completedSettled) return;
|
|
164
184
|
completedSettled = true;
|
|
165
185
|
for (const [signal, handler] of signalHandlers) process.removeListener(signal, handler);
|
|
166
186
|
settleLoaded(false);
|
|
187
|
+
settleReady(false);
|
|
167
188
|
resolveCompleted(result);
|
|
168
189
|
};
|
|
169
190
|
const signalHandlers = new Map();
|
|
@@ -177,23 +198,93 @@ function spawnProtectedCore(args, env, cwd) {
|
|
|
177
198
|
process.once(signal, handler);
|
|
178
199
|
}
|
|
179
200
|
child.on('message', (message) => {
|
|
180
|
-
if (message?.type
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
child.disconnect();
|
|
184
|
-
} catch {}
|
|
201
|
+
if (message?.type === CORE_LOADED_MESSAGE) settleLoaded(true);
|
|
202
|
+
if (message?.type === RUNTIME_READY_MESSAGE) settleReady(true);
|
|
203
|
+
options.onMessage?.(message, child);
|
|
185
204
|
});
|
|
186
205
|
child.once('error', (error) => settleCompleted({ error }));
|
|
187
206
|
child.once('exit', (code, signal) => settleCompleted({ code, signal }));
|
|
188
|
-
return { child, completed, loaded };
|
|
207
|
+
return { child, completed, loaded, packageRoot, ready };
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
async function waitForRuntimeReady(core, timeoutMs = RUNTIME_READY_TIMEOUT_MS) {
|
|
211
|
+
let timer;
|
|
212
|
+
const timedOut = new Promise((resolve) => {
|
|
213
|
+
timer = setTimeout(() => resolve(false), timeoutMs);
|
|
214
|
+
timer.unref?.();
|
|
215
|
+
});
|
|
216
|
+
try {
|
|
217
|
+
return await Promise.race([core.ready, timedOut]);
|
|
218
|
+
} finally {
|
|
219
|
+
clearTimeout(timer);
|
|
220
|
+
}
|
|
189
221
|
}
|
|
190
222
|
|
|
191
|
-
async function superviseProtectedCore(args, env, cwd, releaseNotice) {
|
|
192
|
-
const
|
|
223
|
+
async function superviseProtectedCore(args, env, cwd, releaseNotice, options = {}) {
|
|
224
|
+
const packageRoot = path.resolve(options.packageRoot || PKG);
|
|
225
|
+
let preparedTarget;
|
|
226
|
+
let handoffSessionId;
|
|
227
|
+
let updateStarted = false;
|
|
228
|
+
const startPreparation = (child) => {
|
|
229
|
+
if (updateStarted || options.runningUpdate === false) return;
|
|
230
|
+
updateStarted = true;
|
|
231
|
+
const sharedHome = env.BLUN_SHARED_HOME;
|
|
232
|
+
if (typeof sharedHome !== 'string' || sharedHome.length === 0) return;
|
|
233
|
+
Promise.resolve((options.prepareRunningUpdate || prepareRunningUpdate)({
|
|
234
|
+
currentVersion: JSON.parse(fs.readFileSync(path.join(packageRoot, 'package.json'), 'utf8')).version,
|
|
235
|
+
sharedHome,
|
|
236
|
+
env,
|
|
237
|
+
})).then((target) => {
|
|
238
|
+
if (target === null || child.connected !== true) return;
|
|
239
|
+
preparedTarget = target;
|
|
240
|
+
child.send({ type: RUNNING_UPDATE_PREPARED_MESSAGE, version: target.version });
|
|
241
|
+
}).catch((error) => {
|
|
242
|
+
options.onRunningUpdateError?.(error);
|
|
243
|
+
});
|
|
244
|
+
};
|
|
245
|
+
const core = spawnProtectedCore(args, env, cwd, {
|
|
246
|
+
packageRoot,
|
|
247
|
+
onMessage(message, child) {
|
|
248
|
+
if (message?.type === RUNTIME_READY_MESSAGE) startPreparation(child);
|
|
249
|
+
if (message?.type === RUNNING_UPDATE_HANDOFF_MESSAGE
|
|
250
|
+
&& typeof message.sessionId === 'string'
|
|
251
|
+
&& message.sessionId.length > 0) {
|
|
252
|
+
handoffSessionId = message.sessionId;
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
});
|
|
193
256
|
const loaded = await core.loaded;
|
|
194
257
|
await releaseNotice();
|
|
195
258
|
const result = await core.completed;
|
|
196
259
|
if (result.error) throw result.error;
|
|
260
|
+
if (result.code === RUNNING_UPDATE_HANDOFF_EXIT_CODE
|
|
261
|
+
&& preparedTarget !== undefined
|
|
262
|
+
&& handoffSessionId !== undefined) {
|
|
263
|
+
const handoff = await handoffRuntime({
|
|
264
|
+
target: preparedTarget,
|
|
265
|
+
previous: { packageRoot, version: readPackageVersion() },
|
|
266
|
+
sessionId: handoffSessionId,
|
|
267
|
+
args,
|
|
268
|
+
probeTarget: options.probeRunningUpdate || (() => true),
|
|
269
|
+
stopOld: async () => {},
|
|
270
|
+
startCore: async ({ packageRoot: nextRoot, args: nextArgs }) => {
|
|
271
|
+
const nextCore = spawnProtectedCore(nextArgs, env, cwd, { packageRoot: nextRoot });
|
|
272
|
+
const nextLoaded = await nextCore.loaded;
|
|
273
|
+
const ready = nextLoaded && await waitForRuntimeReady(nextCore, options.readyTimeoutMs);
|
|
274
|
+
if (!ready) {
|
|
275
|
+
try { nextCore.child.kill(); } catch {}
|
|
276
|
+
await nextCore.completed;
|
|
277
|
+
}
|
|
278
|
+
return { ...nextCore, ready };
|
|
279
|
+
},
|
|
280
|
+
activateTarget: (target) => (options.activateRuntime || activateRuntime)(env.BLUN_SHARED_HOME, target),
|
|
281
|
+
});
|
|
282
|
+
const resumed = handoff.core;
|
|
283
|
+
if (resumed === undefined || resumed.ready !== true) return 1;
|
|
284
|
+
const resumedResult = await resumed.completed;
|
|
285
|
+
if (resumedResult.error) throw resumedResult.error;
|
|
286
|
+
return exitCodeForChild(resumedResult, true);
|
|
287
|
+
}
|
|
197
288
|
return exitCodeForChild(result, loaded);
|
|
198
289
|
}
|
|
199
290
|
|
|
@@ -246,8 +337,40 @@ function spawnManagedLauncher(binary, cwd) {
|
|
|
246
337
|
});
|
|
247
338
|
}
|
|
248
339
|
|
|
340
|
+
function spawnActiveLauncher(packageRoot, cwd) {
|
|
341
|
+
const entryName = path.basename(process.argv[1] || 'king.js').toLowerCase() === 'blun.js'
|
|
342
|
+
? 'blun.js'
|
|
343
|
+
: 'king.js';
|
|
344
|
+
return new Promise((resolve, reject) => {
|
|
345
|
+
const child = spawn(process.execPath, [path.join(packageRoot, 'bin', entryName), ...RAW_ARGS], {
|
|
346
|
+
cwd,
|
|
347
|
+
env: { ...process.env, BLUN_ACTIVE_RUNTIME_ROOT: packageRoot },
|
|
348
|
+
stdio: 'inherit',
|
|
349
|
+
windowsHide: true,
|
|
350
|
+
});
|
|
351
|
+
child.once('error', reject);
|
|
352
|
+
child.once('exit', (code, signal) => {
|
|
353
|
+
if (Number.isInteger(code)) resolve(code);
|
|
354
|
+
else if (signal === 'SIGINT') resolve(130);
|
|
355
|
+
else if (signal === 'SIGTERM') resolve(143);
|
|
356
|
+
else resolve(1);
|
|
357
|
+
});
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
|
|
249
361
|
async function runLauncher(options = {}) {
|
|
250
362
|
const callerCwd = process.cwd();
|
|
363
|
+
if (options.skipActiveRuntime !== true) {
|
|
364
|
+
const activeHome = resolveLauncherPrivatePaths().sharedHome;
|
|
365
|
+
const activeRuntime = readActiveRuntime(activeHome);
|
|
366
|
+
const currentVersion = readPackageVersion();
|
|
367
|
+
if (activeRuntime !== null
|
|
368
|
+
&& path.resolve(activeRuntime.packageRoot) !== PKG
|
|
369
|
+
&& compareSemver(currentVersion, activeRuntime.version) === -1) {
|
|
370
|
+
process.exitCode = await spawnActiveLauncher(activeRuntime.packageRoot, callerCwd);
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
251
374
|
const mode = options.mode || launcherModeFromArgv(process.argv);
|
|
252
375
|
const explicitUpdateRequest = isNpmPackageUpdateRequest(ARGS);
|
|
253
376
|
const explicitToolsRequest = String(ARGS[0] || '').toLowerCase() === 'tools';
|
package/bin/profile-runtime.cjs
CHANGED
|
@@ -26,6 +26,8 @@ const LEGACY_PROFILE_ENTRIES = Object.freeze([
|
|
|
26
26
|
'tui.toml',
|
|
27
27
|
]);
|
|
28
28
|
const MIGRATION_MARKER = '.legacy-profile-migrated-v1';
|
|
29
|
+
const SESSION_PATH_MIGRATION_MARKER = '.legacy-profile-session-paths-v2';
|
|
30
|
+
const SESSION_PATH_BACKUP_DIR = '.legacy-profile-session-paths-v2-backup';
|
|
29
31
|
|
|
30
32
|
function normalizeProfileName(value) {
|
|
31
33
|
const normalized = String(value || '').trim().toLowerCase();
|
|
@@ -102,27 +104,161 @@ function copyLegacyEntry(source, target, fsImpl) {
|
|
|
102
104
|
if (stat.isFile()) fsImpl.copyFileSync(source, target);
|
|
103
105
|
}
|
|
104
106
|
|
|
105
|
-
function
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
107
|
+
function relativePathInside(parent, child) {
|
|
108
|
+
const relative = path.relative(path.resolve(parent), path.resolve(child));
|
|
109
|
+
if (!relative || relative === '..' || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
return relative;
|
|
113
|
+
}
|
|
109
114
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
115
|
+
function replaceTextFileIfUnchanged(target, original, replacement, fsImpl) {
|
|
116
|
+
if (fsImpl.readFileSync(target, 'utf8') !== original) {
|
|
117
|
+
throw new Error(`Sitzungsdatei wurde waehrend der Migration geaendert: ${target}`);
|
|
113
118
|
}
|
|
119
|
+
const temporary = `${target}.migration-${process.pid}-${Date.now()}.tmp`;
|
|
120
|
+
fsImpl.writeFileSync(temporary, replacement, 'utf8');
|
|
114
121
|
try {
|
|
115
|
-
fsImpl.
|
|
122
|
+
fsImpl.renameSync(temporary, target);
|
|
116
123
|
} catch (error) {
|
|
117
|
-
|
|
124
|
+
try {
|
|
125
|
+
fsImpl.rmSync(temporary, { force: true });
|
|
126
|
+
} catch {}
|
|
127
|
+
throw error;
|
|
118
128
|
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function backupFile(source, target, fsImpl) {
|
|
132
|
+
if (fsImpl.existsSync(target)) return;
|
|
133
|
+
fsImpl.mkdirSync(path.dirname(target), { recursive: true });
|
|
134
|
+
fsImpl.copyFileSync(source, target);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function rewriteSessionState(statePath, legacySessionDir, profileSessionDir, backupRoot, relativeSessionDir, fsImpl) {
|
|
138
|
+
if (!fsImpl.existsSync(statePath)) return false;
|
|
139
|
+
const original = fsImpl.readFileSync(statePath, 'utf8');
|
|
140
|
+
let state;
|
|
141
|
+
try {
|
|
142
|
+
state = JSON.parse(original);
|
|
143
|
+
} catch {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
if (!state || typeof state !== 'object' || Array.isArray(state) || !state.agents || typeof state.agents !== 'object') {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
let changed = false;
|
|
151
|
+
const agents = {};
|
|
152
|
+
for (const [agentId, value] of Object.entries(state.agents)) {
|
|
153
|
+
if (!value || typeof value !== 'object' || Array.isArray(value) || typeof value.homedir !== 'string') {
|
|
154
|
+
agents[agentId] = value;
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
const relativeAgentPath = relativePathInside(legacySessionDir, value.homedir);
|
|
158
|
+
if (relativeAgentPath === null) {
|
|
159
|
+
agents[agentId] = value;
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
agents[agentId] = {
|
|
163
|
+
...value,
|
|
164
|
+
homedir: path.join(profileSessionDir, relativeAgentPath),
|
|
165
|
+
};
|
|
166
|
+
changed = true;
|
|
167
|
+
}
|
|
168
|
+
if (!changed) return false;
|
|
169
|
+
|
|
170
|
+
backupFile(statePath, path.join(backupRoot, 'sessions', relativeSessionDir, 'state.json'), fsImpl);
|
|
171
|
+
const replacement = `${JSON.stringify({ ...state, agents }, null, 2)}\n`;
|
|
172
|
+
replaceTextFileIfUnchanged(statePath, original, replacement, fsImpl);
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function repairDefaultProfileSessionPaths(sharedHome, fsImpl = fs, options = {}) {
|
|
177
|
+
const paths = resolveProfilePaths(sharedHome, DEFAULT_PROFILE);
|
|
178
|
+
const marker = path.join(paths.home, SESSION_PATH_MIGRATION_MARKER);
|
|
179
|
+
const filteredWorkDir = options.workDir === undefined ? null : path.resolve(options.workDir);
|
|
180
|
+
if (filteredWorkDir === null && fsImpl.existsSync(marker)) return paths;
|
|
181
|
+
if (!fsImpl.existsSync(paths.sessionIndex)) {
|
|
182
|
+
if (filteredWorkDir === null) fsImpl.writeFileSync(marker, 'legacy session paths repaired once\n', 'utf8');
|
|
183
|
+
return paths;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const legacySessions = path.join(sharedHome, 'sessions');
|
|
187
|
+
const backupRoot = path.join(paths.home, SESSION_PATH_BACKUP_DIR);
|
|
188
|
+
const original = fsImpl.readFileSync(paths.sessionIndex, 'utf8');
|
|
189
|
+
const output = [];
|
|
190
|
+
let changed = false;
|
|
191
|
+
for (const line of original.split(/\r?\n/u)) {
|
|
192
|
+
if (line.trim() === '') {
|
|
193
|
+
output.push(line);
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
let entry;
|
|
197
|
+
try {
|
|
198
|
+
entry = JSON.parse(line);
|
|
199
|
+
} catch {
|
|
200
|
+
output.push(line);
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
if (!entry || typeof entry !== 'object' || typeof entry.sessionId !== 'string'
|
|
204
|
+
|| typeof entry.sessionDir !== 'string' || typeof entry.workDir !== 'string'
|
|
205
|
+
|| (filteredWorkDir !== null && path.resolve(entry.workDir) !== filteredWorkDir)) {
|
|
206
|
+
output.push(line);
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
const relativeSessionDir = relativePathInside(legacySessions, entry.sessionDir);
|
|
210
|
+
if (relativeSessionDir === null || path.basename(relativeSessionDir) !== entry.sessionId) {
|
|
211
|
+
output.push(line);
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
const profileSessionDir = path.join(paths.sessions, relativeSessionDir);
|
|
215
|
+
if (!fsImpl.existsSync(profileSessionDir)) {
|
|
216
|
+
output.push(line);
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
rewriteSessionState(
|
|
221
|
+
path.join(profileSessionDir, 'state.json'),
|
|
222
|
+
path.resolve(entry.sessionDir),
|
|
223
|
+
profileSessionDir,
|
|
224
|
+
backupRoot,
|
|
225
|
+
relativeSessionDir,
|
|
226
|
+
fsImpl,
|
|
227
|
+
);
|
|
228
|
+
output.push(JSON.stringify({ ...entry, sessionDir: profileSessionDir }));
|
|
229
|
+
changed = true;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (changed) {
|
|
233
|
+
backupFile(paths.sessionIndex, path.join(backupRoot, 'session_index.jsonl'), fsImpl);
|
|
234
|
+
replaceTextFileIfUnchanged(paths.sessionIndex, original, output.join('\n'), fsImpl);
|
|
235
|
+
}
|
|
236
|
+
if (filteredWorkDir === null) fsImpl.writeFileSync(marker, 'legacy session paths repaired once\n', 'utf8');
|
|
119
237
|
return paths;
|
|
120
238
|
}
|
|
121
239
|
|
|
240
|
+
function seedDefaultProfileFromLegacy(sharedHome, fsImpl = fs) {
|
|
241
|
+
const paths = resolveProfilePaths(sharedHome, DEFAULT_PROFILE);
|
|
242
|
+
const marker = path.join(paths.home, MIGRATION_MARKER);
|
|
243
|
+
fsImpl.mkdirSync(paths.home, { recursive: true });
|
|
244
|
+
if (!fsImpl.existsSync(marker)) {
|
|
245
|
+
for (const entry of LEGACY_PROFILE_ENTRIES) {
|
|
246
|
+
copyLegacyEntry(path.join(sharedHome, entry), path.join(paths.home, entry), fsImpl);
|
|
247
|
+
}
|
|
248
|
+
try {
|
|
249
|
+
fsImpl.writeFileSync(marker, 'legacy profile state copied once\n', { encoding: 'utf8', flag: 'wx' });
|
|
250
|
+
} catch (error) {
|
|
251
|
+
if (error?.code !== 'EEXIST') throw error;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return repairDefaultProfileSessionPaths(sharedHome, fsImpl);
|
|
255
|
+
}
|
|
256
|
+
|
|
122
257
|
module.exports = {
|
|
123
258
|
DEFAULT_PROFILE,
|
|
124
259
|
normalizeProfileName,
|
|
125
260
|
parseProfileLaunchArgs,
|
|
261
|
+
repairDefaultProfileSessionPaths,
|
|
126
262
|
resolveProfilePaths,
|
|
127
263
|
seedDefaultProfileFromLegacy,
|
|
128
264
|
};
|