ccakashic 0.3.0 → 0.3.1

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.
@@ -176,36 +176,42 @@ async function handleResume(req, res) {
176
176
  if (!project || !sessionPath || !fs.existsSync(sessionPath)) {
177
177
  return respond(404, { action: 'error', message: 'Session not found' });
178
178
  }
179
- // Already open via a previous resume → jump instead of forking.
180
- const liveWorkspace = await (0, cmux_1.findLiveWorkspaceForSession)(sessionId);
181
- if (liveWorkspace) {
182
- try {
183
- await (0, cmux_1.selectWorkspace)(liveWorkspace);
184
- return respond(200, { action: 'jumped', workspace: liveWorkspace });
185
- }
186
- catch {
187
- // workspace died between the check and the select; fall through
188
- }
189
- }
190
179
  const cwd = await (0, discover_1.readCwdFromSession)(sessionPath);
191
180
  if (!cwd)
192
181
  return respond(200, { action: 'error', message: 'No cwd recorded in this session' });
193
182
  if (!fs.existsSync(cwd)) {
194
183
  return respond(200, { action: 'error', message: `Directory no longer exists: ${cwd}` });
195
184
  }
196
- if (NO_CMUX || !(await (0, cmux_1.isCmuxAvailable)())) {
197
- return respond(200, { action: 'unavailable', command: (0, cmux_1.buildResumeCommand)(cwd, sessionId) });
185
+ const command = (0, cmux_1.buildResumeCommand)(cwd, sessionId);
186
+ if (NO_CMUX) {
187
+ return respond(200, { action: 'unavailable', command });
198
188
  }
199
- const sessions = await (0, discover_1.listSessions)(project.dir);
200
- const preview = sessions.find((s) => s.id === sessionId);
201
- const title = preview?.customTitle || preview?.aiTitle || preview?.slug || sessionId.slice(0, 8);
189
+ // Any cmux call can fail if the long-running server has lost contact with the
190
+ // current cmux instance (Mac sleep/wake, cmux restart). Rather than surface a
191
+ // hard error or hide the button, fall back to handing back the copy command
192
+ // with a hint to restart ccakashic if it persists.
202
193
  try {
194
+ // Already open via a previous resume → jump instead of forking.
195
+ const liveWorkspace = await (0, cmux_1.findLiveWorkspaceForSession)(sessionId);
196
+ if (liveWorkspace) {
197
+ await (0, cmux_1.selectWorkspace)(liveWorkspace);
198
+ return respond(200, { action: 'jumped', workspace: liveWorkspace });
199
+ }
200
+ const sessions = await (0, discover_1.listSessions)(project.dir);
201
+ const preview = sessions.find((s) => s.id === sessionId);
202
+ const title = preview?.customTitle || preview?.aiTitle || preview?.slug || sessionId.slice(0, 8);
203
203
  const result = await (0, cmux_1.resumeInNewWorkspace)(cwd, sessionId, title, mode === 'background');
204
204
  (0, cmux_1.saveResumeMapEntry)(sessionId, result.workspaceId);
205
205
  return respond(200, { action: 'resumed', workspace: result.workspaceId });
206
206
  }
207
207
  catch (err) {
208
- return respond(200, { action: 'error', message: `cmux error: ${err?.message || err}` });
208
+ if (process.env.CCAKASHIC_DEBUG)
209
+ console.error('[resume] cmux failed:', err?.message || err);
210
+ return respond(200, {
211
+ action: 'unavailable',
212
+ command,
213
+ message: 'cmux unreachable — copied the command instead. If this persists, restart ccakashic.',
214
+ });
209
215
  }
210
216
  }
211
217
  // Only accept loopback Host headers. The server binds 127.0.0.1, but without
package/dist/cmux.js CHANGED
@@ -93,20 +93,30 @@ function run(args, timeoutMs = 5000) {
93
93
  });
94
94
  });
95
95
  }
96
- let cachedAvailable = null;
96
+ let everAvailable = false;
97
+ let cachedUnavailableAt = 0;
97
98
  async function isCmuxAvailable() {
98
- // ping spawns a process; cache briefly so page renders stay cheap
99
- if (cachedAvailable && Date.now() - cachedAvailable.at < 10_000) {
100
- return cachedAvailable.value;
101
- }
99
+ // Sticky-positive: once cmux has answered, keep reporting available for the
100
+ // life of the process. A long-running server can transiently lose contact
101
+ // with cmux (Mac sleep/wake, cmux app restart) and `ping` then fails — but we
102
+ // must NOT hide the Resume UI on such a blip. The buttons stay; if cmux is
103
+ // genuinely unreachable at click time, /api/resume degrades to the copy
104
+ // command. Negative results are cached only ~5s so first contact is quick.
105
+ if (everAvailable)
106
+ return true;
107
+ if (cachedUnavailableAt && Date.now() - cachedUnavailableAt < 5_000)
108
+ return false;
102
109
  let value = false;
103
110
  try {
104
- value = (await run(['ping'], 1500)) === 'PONG';
111
+ value = (await run(['ping'], 2500)) === 'PONG';
105
112
  }
106
113
  catch {
107
114
  value = false;
108
115
  }
109
- cachedAvailable = { value, at: Date.now() };
116
+ if (value)
117
+ everAvailable = true;
118
+ else
119
+ cachedUnavailableAt = Date.now();
110
120
  return value;
111
121
  }
112
122
  function shellQuote(s) {
package/dist/resume-ui.js CHANGED
@@ -89,9 +89,9 @@ function resumeJS(ctx) {
89
89
  clearTimeout(toastEl._t);
90
90
  toastEl._t = setTimeout(function() { toastEl.classList.remove('visible'); }, 2500);
91
91
  }
92
- function copy(text) {
92
+ function copy(text, okMsg) {
93
93
  navigator.clipboard.writeText(text).then(
94
- function() { toast('Command copied'); },
94
+ function() { toast(okMsg || 'Command copied'); },
95
95
  function() { toast('Copy failed'); }
96
96
  );
97
97
  }
@@ -134,7 +134,7 @@ function resumeJS(ctx) {
134
134
  btn.classList.add('is-open');
135
135
  btn.innerHTML = '&#8618; Jump';
136
136
  } else if (data.action === 'unavailable' && data.command) {
137
- copy(data.command);
137
+ copy(data.command, data.message);
138
138
  } else {
139
139
  toast(data.message || 'Resume failed');
140
140
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccakashic",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "A cross-project dashboard for your Claude Code sessions (~/.claude/projects/) — browse logs as beautiful HTML, see which sessions are waiting for you, and resume any of them in one click via cmux",
5
5
  "bin": {
6
6
  "ccakashic": "dist/bin/ccakashic.js"