claude-code-session-manager 0.91.0 → 0.92.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/dist/assets/{AgentLibrary-zkgdnCnG.js → AgentLibrary-Bu-1eT_n.js} +1 -1
- package/dist/assets/{DataModel-CNFdqxrR.js → DataModel-Bk9rkJ8x.js} +1 -1
- package/dist/assets/{History-C-qijfXz.js → History-BB6wpf02.js} +1 -1
- package/dist/assets/{Hooks-DGxt1nBd.js → Hooks-zQZH56Sv.js} +1 -1
- package/dist/assets/{HostBilko-sIYTK-m9.js → HostBilko-CW0pHoPS.js} +1 -1
- package/dist/assets/{Library-51Jx0oGl.js → Library-DrGdaz9N.js} +1 -1
- package/dist/assets/{ListDetail-ByNJU-ie.js → ListDetail-D4bxU_-2.js} +1 -1
- package/dist/assets/{MarkdownEditor-Wwcpe9O1.js → MarkdownEditor-H1ytN4Tk.js} +1 -1
- package/dist/assets/{McpServers-Bs5xPbTG.js → McpServers-CYSRCI-j.js} +1 -1
- package/dist/assets/{Memory-BsHGInmd.js → Memory-zMNpJyRU.js} +1 -1
- package/dist/assets/{Panel-ilEDcV0m.js → Panel-RMz3Acpu.js} +1 -1
- package/dist/assets/{Permissions-Djgc3TFk.js → Permissions-Be6cIkPg.js} +1 -1
- package/dist/assets/{Plugins-BBojr1mt.js → Plugins-B8sTWf2K.js} +2 -2
- package/dist/assets/{ProvenanceBadge-Cz1VLnXb.js → ProvenanceBadge-CO-DZN38.js} +1 -1
- package/dist/assets/{SaveBar-Cj0wz84u.js → SaveBar-xQZ2EBOn.js} +1 -1
- package/dist/assets/{Scheduler-D4Ekm93R.js → Scheduler-CF3zsJBw.js} +1 -1
- package/dist/assets/{ScopeSwitcher-DA5MqWli.js → ScopeSwitcher-BAfquN6Y.js} +1 -1
- package/dist/assets/{Settings-B1Kfgjvl.js → Settings-uPjmqutA.js} +1 -1
- package/dist/assets/{SkillReferenceGraph-Dew-1uMy.js → SkillReferenceGraph-BI1YtCbB.js} +1 -1
- package/dist/assets/{Skills-DzuhnXy9.js → Skills-BEy9FDYd.js} +1 -1
- package/dist/assets/{SystemPrompt-CNcCwEev.js → SystemPrompt-BYkkw-LL.js} +1 -1
- package/dist/assets/{TagLibrary-D0plTZgP.js → TagLibrary-Kg3yOC5b.js} +1 -1
- package/dist/assets/{TiptapBody-BlmUt7SR.js → TiptapBody-BptXAVR9.js} +1 -1
- package/dist/assets/{Toggle-yTfx2R-e.js → Toggle-BwiClCSI.js} +1 -1
- package/dist/assets/{index-CRPJslSA.css → index-D3P_jldk.css} +1 -1
- package/dist/assets/{index-ju-h7BFf.js → index-a2MVk8qh.js} +329 -329
- package/dist/assets/{settingsSchema-BkcnLBYk.js → settingsSchema-DgxlStF5.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/main/__tests__/chatRunner-session-flag-retry.test.cjs +52 -3
- package/src/main/__tests__/pty-epic-worktree-spawn-cwd.test.cjs +40 -0
- package/src/main/build-info.json +4 -4
- package/src/main/chatRunner.cjs +27 -4
- package/src/main/config.cjs +5 -0
- package/src/main/docEdit.cjs +3 -2
- package/src/main/index.cjs +2 -0
- package/src/main/lib/__tests__/epicSpawnCwd.test.cjs +58 -2
- package/src/main/lib/__tests__/epicSpawnPlan.test.cjs +79 -0
- package/src/main/lib/__tests__/updateCheck.test.cjs +63 -0
- package/src/main/lib/epicSpawnCwd.cjs +22 -13
- package/src/main/lib/epicSpawnPlan.cjs +27 -12
- package/src/main/lib/epicTranscriptPath.cjs +3 -2
- package/src/main/lib/epicWorktreeMerge.cjs +4 -4
- package/src/main/lib/updateCheck.cjs +102 -0
- package/src/main/pty.cjs +19 -7
- package/src/preload/api.d.ts +2 -0
- package/src/preload/index.cjs +1 -0
|
@@ -17,10 +17,10 @@
|
|
|
17
17
|
* On success the worktree checkout is torn down (cleanupEpicWorktree) since
|
|
18
18
|
* its content now safely lives on the main tree. On a real conflict, the
|
|
19
19
|
* branch and worktree directory are left intact — never silently discarded
|
|
20
|
-
* — so a human can still open a Terminal into `worktree.dir` (
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
20
|
+
* — so a human can still open a Terminal into `worktree.dir` (the spawn
|
|
21
|
+
* resolution in epicSpawnCwd.cjs is status-aware: `needs_merge_resolution`
|
|
22
|
+
* with an existing dir still lands there, while `merged`/`disabled` skip the
|
|
23
|
+
* re-attach and fall back to the project cwd) and resolve it manually before
|
|
24
24
|
* re-running this same action.
|
|
25
25
|
*/
|
|
26
26
|
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* updateCheck.cjs — bounded, offline-safe, opt-out "is a newer build published?" probe.
|
|
5
|
+
*
|
|
6
|
+
* getUpdateStatus() → { current, latest, behind }. Never throws/rejects; any failure
|
|
7
|
+
* (offline, DNS, non-200, bad JSON, timeout) → { current, latest: null, behind: false }.
|
|
8
|
+
* The request is a bare GET (no headers/cookies/query/body) so it carries nothing that
|
|
9
|
+
* identifies the user or machine. Disabled by SM_UPDATE_CHECK=0 or E2E mode (SM_E2E=1).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const https = require('node:https');
|
|
13
|
+
const fs = require('node:fs');
|
|
14
|
+
const path = require('node:path');
|
|
15
|
+
|
|
16
|
+
const CACHE_TTL_MS = 6 * 60 * 60 * 1000;
|
|
17
|
+
const REQUEST_TIMEOUT_MS = 3000;
|
|
18
|
+
const CACHE_FILE = 'update-check.json';
|
|
19
|
+
|
|
20
|
+
/** Plain GET → parsed JSON. Rejects on non-200, bad JSON, or timeout. */
|
|
21
|
+
function httpGetJson(url, timeoutMs) {
|
|
22
|
+
return new Promise((resolve, reject) => {
|
|
23
|
+
const req = https.get(url, { timeout: timeoutMs }, (res) => {
|
|
24
|
+
if (res.statusCode !== 200) {
|
|
25
|
+
res.resume();
|
|
26
|
+
reject(new Error(`HTTP ${res.statusCode}`));
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
let body = '';
|
|
30
|
+
res.setEncoding('utf8');
|
|
31
|
+
res.on('data', (c) => {
|
|
32
|
+
body += c;
|
|
33
|
+
if (body.length > 65536) req.destroy(new Error('response too large'));
|
|
34
|
+
});
|
|
35
|
+
res.on('end', () => {
|
|
36
|
+
try { resolve(JSON.parse(body)); } catch (e) { reject(e); }
|
|
37
|
+
});
|
|
38
|
+
res.on('error', reject);
|
|
39
|
+
});
|
|
40
|
+
req.on('timeout', () => req.destroy(new Error('timeout')));
|
|
41
|
+
req.on('error', reject);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function parseTriple(v) {
|
|
46
|
+
const m = /^v?(\d+)\.(\d+)\.(\d+)/.exec(String(v || ''));
|
|
47
|
+
return m ? [Number(m[1]), Number(m[2]), Number(m[3])] : null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** True iff `current` is strictly older than `latest` (numeric major.minor.patch). */
|
|
51
|
+
function isBehind(current, latest) {
|
|
52
|
+
const a = parseTriple(current);
|
|
53
|
+
const b = parseTriple(latest);
|
|
54
|
+
if (!a || !b) return false;
|
|
55
|
+
for (let i = 0; i < 3; i++) {
|
|
56
|
+
if (a[i] !== b[i]) return a[i] < b[i];
|
|
57
|
+
}
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function readCache(file, now) {
|
|
62
|
+
try {
|
|
63
|
+
const c = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
64
|
+
if (c && typeof c.latest === 'string' && typeof c.checkedAt === 'number'
|
|
65
|
+
&& now - c.checkedAt >= 0 && now - c.checkedAt < CACHE_TTL_MS) return c.latest;
|
|
66
|
+
} catch { /* missing/corrupt cache = miss */ }
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function defaultPackageName() {
|
|
71
|
+
return require(path.join(__dirname, '..', '..', '..', 'package.json')).name;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async function getUpdateStatus({ deps } = {}) {
|
|
75
|
+
const d = deps || {};
|
|
76
|
+
let current = '';
|
|
77
|
+
try {
|
|
78
|
+
const app = d.app || require('electron').app;
|
|
79
|
+
current = app.getVersion();
|
|
80
|
+
const env = d.env || process.env;
|
|
81
|
+
if (env.SM_UPDATE_CHECK === '0' || env.SM_E2E === '1') return { current, latest: null, behind: false };
|
|
82
|
+
|
|
83
|
+
const now = (d.now || Date.now)();
|
|
84
|
+
const file = path.join(app.getPath('userData'), CACHE_FILE);
|
|
85
|
+
let latest = readCache(file, now);
|
|
86
|
+
if (latest == null) {
|
|
87
|
+
const name = (d.packageName || defaultPackageName)();
|
|
88
|
+
const url = `https://registry.npmjs.org/-/package/${name}/dist-tags`;
|
|
89
|
+
const tags = await (d.fetchJson || httpGetJson)(url, REQUEST_TIMEOUT_MS);
|
|
90
|
+
latest = tags && typeof tags.latest === 'string' ? tags.latest : null;
|
|
91
|
+
if (latest == null) return { current, latest: null, behind: false };
|
|
92
|
+
try {
|
|
93
|
+
await (d.writeJson || require('../config.cjs').writeJson)(file, { checkedAt: now, latest });
|
|
94
|
+
} catch { /* cache is best-effort */ }
|
|
95
|
+
}
|
|
96
|
+
return { current, latest, behind: isBehind(current, latest) };
|
|
97
|
+
} catch {
|
|
98
|
+
return { current, latest: null, behind: false };
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
module.exports = { getUpdateStatus, isBehind };
|
package/src/main/pty.cjs
CHANGED
|
@@ -22,7 +22,7 @@ const { cleanChildEnv, pathWithUserBins } = require('./lib/cleanEnv.cjs');
|
|
|
22
22
|
const { checkInsideHome } = require('./lib/insideHome.cjs');
|
|
23
23
|
const { sendIfAlive } = require('./lib/sendToRenderer.cjs');
|
|
24
24
|
const opsErrorLog = require('./lib/opsErrorLog.cjs');
|
|
25
|
-
const {
|
|
25
|
+
const { planEpicSpawn } = require('./lib/epicSpawnPlan.cjs');
|
|
26
26
|
const telemetryCounters = require('./lib/telemetryCounters.cjs');
|
|
27
27
|
|
|
28
28
|
// Absolute path to the installed package root (src/main/ -> ../../), shown in
|
|
@@ -176,12 +176,24 @@ class PtyManager {
|
|
|
176
176
|
|
|
177
177
|
const shell = process.env.SHELL || '/bin/bash';
|
|
178
178
|
console.log('[pty] spawning shell', shell);
|
|
179
|
-
// Tab ID = claudeSessionId (see CLAUDE.md),
|
|
180
|
-
// this
|
|
181
|
-
//
|
|
182
|
-
//
|
|
183
|
-
|
|
184
|
-
|
|
179
|
+
// Tab ID = claudeSessionId (see CLAUDE.md), and Chat/Terminal are two views over ONE session,
|
|
180
|
+
// so this asks the same decision function chatRunner.cjs does (epicSpawnPlan.cjs). `cwd`
|
|
181
|
+
// itself is NEVER repointed; only this actual PTY spawn option is (epicSpawnCwd.cjs's
|
|
182
|
+
// ops-root hazard). A refused plan is surfaced in-tab rather than spawning into a bad cwd.
|
|
183
|
+
const plan = planEpicSpawn({ cwd, claudeSessionId: tabId });
|
|
184
|
+
if (!plan.ok) {
|
|
185
|
+
opsErrorLog.appendError({
|
|
186
|
+
cwd,
|
|
187
|
+
scope: 'pty',
|
|
188
|
+
tabId,
|
|
189
|
+
tags: ['spawn-refused', plan.code],
|
|
190
|
+
message: plan.message,
|
|
191
|
+
});
|
|
192
|
+
sendIfAlive(this.window, `pty:data:${tabId}`, `\r\n${plan.message}\r\n`);
|
|
193
|
+
setImmediate(() => sendIfAlive(this.window, `pty:exit:${tabId}`, { exitCode: 1, signal: undefined }));
|
|
194
|
+
return { pid: null, cwd, reattached: false, error: plan.message };
|
|
195
|
+
}
|
|
196
|
+
const spawnCwd = plan.execCwd;
|
|
185
197
|
if (spawnCwd !== cwd) {
|
|
186
198
|
console.log(`[pty] tabId=${tabId} isolated in worktree ${spawnCwd}`);
|
|
187
199
|
}
|
package/src/preload/api.d.ts
CHANGED
|
@@ -1541,6 +1541,8 @@ export interface ChatExternalSendEvent {
|
|
|
1541
1541
|
export interface SessionManagerAPI {
|
|
1542
1542
|
app: {
|
|
1543
1543
|
version: () => Promise<string>;
|
|
1544
|
+
/** Published-`latest` probe (cached 6h, opt-out SM_UPDATE_CHECK=0). Never rejects; offline → latest null. */
|
|
1545
|
+
updateStatus: () => Promise<{ current: string; latest: string | null; behind: boolean }>;
|
|
1544
1546
|
homeDir: () => Promise<string>;
|
|
1545
1547
|
cwd: () => Promise<string>;
|
|
1546
1548
|
engageRulesPath: () => Promise<string | null>;
|
package/src/preload/index.cjs
CHANGED
|
@@ -3,6 +3,7 @@ const { contextBridge, ipcRenderer } = require('electron');
|
|
|
3
3
|
contextBridge.exposeInMainWorld('api', {
|
|
4
4
|
app: {
|
|
5
5
|
version: () => ipcRenderer.invoke('app:version'),
|
|
6
|
+
updateStatus: () => ipcRenderer.invoke('app:update-status'),
|
|
6
7
|
homeDir: () => ipcRenderer.invoke('app:home-dir'),
|
|
7
8
|
cwd: () => ipcRenderer.invoke('app:cwd'),
|
|
8
9
|
engageRulesPath: () => ipcRenderer.invoke('app:engage-rules-path'),
|