claude-code-hub 1.17.0 → 1.18.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-code-hub",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.18.0",
|
|
4
4
|
"description": "Unified hub for Claude Code tools — Marketplace + Kanban in one PWA",
|
|
5
5
|
"main": "server.js",
|
|
6
6
|
"bin": {
|
|
@@ -46,8 +46,8 @@
|
|
|
46
46
|
},
|
|
47
47
|
"homepage": "https://github.com/NikiforovAll/claude-code-hub#readme",
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"claude-code-cost": "1.
|
|
50
|
-
"claude-code-kanban": "7.
|
|
49
|
+
"claude-code-cost": "1.4.0",
|
|
50
|
+
"claude-code-kanban": "7.14.0",
|
|
51
51
|
"claude-code-marketplace": "1.6.0",
|
|
52
52
|
"claude-code-memory-explorer": "1.3.2",
|
|
53
53
|
"express": "^4.21.0",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-code-hub",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.11",
|
|
4
4
|
"description": "Claude Code Hub integration: a skill to build on and extend the hub, with links to its docs, the extension mechanism and custom themes",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "NikiforovAll"
|
|
@@ -75,7 +75,7 @@ The SDK writes the hub's color variables (`--accent`, `--bg-surface`, …) on `d
|
|
|
75
75
|
Apps never talk to each other directly. Each page talks to the hub page through the SDK, and the hub passes the message on. There is no server-to-server channel. Page: `extensibility/reference/connect/`.
|
|
76
76
|
|
|
77
77
|
- **Topics: one to many, sticky.** The hub keeps the last value and replays it when an app connects or comes on screen. The hub publishes `project.changed` (`{project, encoded, name}` or `null`) and `theme.changed`. An app publishes only the topics its manifest lists in `publishes`. Kanban publishes `session.changed`. Subscribe before the page's `load` event, because the SDK sends the topic list with its hello.
|
|
78
|
-
- **Actions: one caller, one handler.** The caller names the action, not the app: `hub.can(name)` decides whether to show the control, `hub.invoke(name, params)` routes it and resolves when routed. The handler declares it in `actions.handles` with a `url` template; with `mode: "message"` a live page gets it through `hub.handle`, else the hub loads the URL, so the handler also reads the params from its query string. The built-in actions are `session.cost` (Cost), `project.plugins` (Marketplace) and `project.memory` (Memory Diagnoser).
|
|
78
|
+
- **Actions: one caller, one handler.** The caller names the action, not the app: `hub.can(name)` decides whether to show the control, `hub.invoke(name, params)` routes it and resolves when routed. The handler declares it in `actions.handles` with a `url` template; with `mode: "message"` a live page gets it through `hub.handle`, else the hub loads the URL, so the handler also reads the params from its query string. The built-in actions are `session.cost` and `agent.cost` (Cost), `project.plugins` (Marketplace) and `project.memory` (Memory Diagnoser).
|
|
79
79
|
- **Capabilities: one provider per job.** `provides.projects.path` answers the project palette's list; `provides.terminal.liveWork` answers the open terminals and makes the app the terminal provider, which gets the terminal env and token. Kanban provides both today.
|
|
80
80
|
|
|
81
81
|
## First in tab order wins
|
package/public/app.js
CHANGED
|
@@ -296,6 +296,23 @@ async function init() {
|
|
|
296
296
|
registerSW();
|
|
297
297
|
bindErrorToast();
|
|
298
298
|
showUpdateToast();
|
|
299
|
+
reloadOnHubRestart();
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// EventSource reconnects by itself while the hub is down, but gives up for good on a non-200 answer.
|
|
303
|
+
function reloadOnHubRestart() {
|
|
304
|
+
let boot = null;
|
|
305
|
+
const connect = () => {
|
|
306
|
+
const events = new EventSource('/api/events');
|
|
307
|
+
events.addEventListener('boot', (e) => {
|
|
308
|
+
if (boot && e.data !== boot) location.reload();
|
|
309
|
+
boot = e.data;
|
|
310
|
+
});
|
|
311
|
+
events.addEventListener('error', () => {
|
|
312
|
+
if (events.readyState === EventSource.CLOSED) setTimeout(connect, 2000);
|
|
313
|
+
});
|
|
314
|
+
};
|
|
315
|
+
connect();
|
|
299
316
|
}
|
|
300
317
|
|
|
301
318
|
// One key, overwritten by each dismissal: a dismissed version stays hidden until a newer one ships.
|
package/public/sw.js
CHANGED
|
@@ -18,6 +18,8 @@ self.addEventListener('activate', (e) => {
|
|
|
18
18
|
});
|
|
19
19
|
|
|
20
20
|
self.addEventListener('fetch', (e) => {
|
|
21
|
+
// A stream held by the worker ends when Chrome stops the idle worker.
|
|
22
|
+
if (new URL(e.request.url).pathname === '/api/events') return;
|
|
21
23
|
if (e.request.url.includes('/api/')) {
|
|
22
24
|
e.respondWith(fetch(e.request));
|
|
23
25
|
return;
|
package/server.js
CHANGED
|
@@ -941,6 +941,14 @@ const updateCheck = createUpdateCheck({
|
|
|
941
941
|
enabled: !process.env.CLAUDE_HUB_NO_UPDATE_CHECK && !fs.existsSync(path.join(__dirname, '.git')),
|
|
942
942
|
});
|
|
943
943
|
|
|
944
|
+
// The page holds this open and reloads when a new run answers, because the tray cannot reach its window.
|
|
945
|
+
const BOOT_ID = crypto.randomBytes(8).toString('hex');
|
|
946
|
+
app.get('/api/events', (_req, res) => {
|
|
947
|
+
res.set({ 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-store' });
|
|
948
|
+
res.flushHeaders();
|
|
949
|
+
res.write(`retry: 2000\nevent: boot\ndata: ${BOOT_ID}\n\n`);
|
|
950
|
+
});
|
|
951
|
+
|
|
944
952
|
app.get('/api/update', async (_req, res) => {
|
|
945
953
|
res.json({ version: HUB_VERSION, update: await updateCheck.update() });
|
|
946
954
|
});
|