claudeck 1.0.1 → 1.0.3
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/README.md +5 -2
- package/cli.js +66 -1
- package/package.json +1 -1
- package/public/index.html +5 -0
- package/public/js/core/api.js +13 -0
- package/public/js/core/dom.js +1 -0
- package/public/js/features/projects.js +30 -0
- package/server.js +15 -1
package/README.md
CHANGED
|
@@ -24,16 +24,19 @@
|
|
|
24
24
|
# One-command launch (no install needed)
|
|
25
25
|
npx claudeck
|
|
26
26
|
|
|
27
|
+
# Custom port
|
|
28
|
+
npx claudeck --port 3000
|
|
29
|
+
|
|
27
30
|
# Or install globally
|
|
28
31
|
npm install -g claudeck
|
|
29
32
|
claudeck
|
|
30
33
|
```
|
|
31
34
|
|
|
32
|
-
|
|
35
|
+
On first run, Claudeck will ask you to choose a port (default: `9009`), then open your browser to the URL shown in the terminal. The port is saved to `~/.claudeck/.env` for future runs.
|
|
33
36
|
|
|
34
37
|
> Requires **Node.js 18+** and Claude Code CLI authentication (`claude auth login`).
|
|
35
38
|
|
|
36
|
-
|
|
39
|
+
User data lives in `~/.claudeck/` (config, database, plugins) — safe for NPX upgrades.
|
|
37
40
|
|
|
38
41
|
---
|
|
39
42
|
|
package/cli.js
CHANGED
|
@@ -1,2 +1,67 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
2
|
+
import { homedir } from "os";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
5
|
+
import { createInterface } from "readline";
|
|
6
|
+
|
|
7
|
+
const DEFAULT_PORT = 9009;
|
|
8
|
+
const envDir = process.env.CLAUDECK_HOME || join(homedir(), ".claudeck");
|
|
9
|
+
const envPath = join(envDir, ".env");
|
|
10
|
+
mkdirSync(envDir, { recursive: true });
|
|
11
|
+
|
|
12
|
+
function readEnv() {
|
|
13
|
+
try { return readFileSync(envPath, "utf-8"); } catch { return ""; }
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function savePort(port) {
|
|
17
|
+
let content = readEnv();
|
|
18
|
+
if (/^PORT=.*/m.test(content)) {
|
|
19
|
+
content = content.replace(/^PORT=.*/m, `PORT=${port}`);
|
|
20
|
+
} else {
|
|
21
|
+
content = content.trimEnd() + `\nPORT=${port}\n`;
|
|
22
|
+
}
|
|
23
|
+
writeFileSync(envPath, content);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function getSavedPort() {
|
|
27
|
+
const match = readEnv().match(/^PORT=(\d+)/m);
|
|
28
|
+
return match ? match[1] : null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function ask(question) {
|
|
32
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
33
|
+
return new Promise(resolve => {
|
|
34
|
+
rl.question(question, answer => { rl.close(); resolve(answer.trim()); });
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function main() {
|
|
39
|
+
// --port flag takes priority
|
|
40
|
+
const portArg = process.argv.find(a => a.startsWith('--port'));
|
|
41
|
+
if (portArg) {
|
|
42
|
+
const port = portArg.includes('=') ? portArg.split('=')[1] : process.argv[process.argv.indexOf(portArg) + 1];
|
|
43
|
+
if (port) {
|
|
44
|
+
process.env.PORT = port;
|
|
45
|
+
savePort(port);
|
|
46
|
+
return import("./server.js");
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// If port already saved, use it
|
|
51
|
+
const saved = getSavedPort();
|
|
52
|
+
if (saved) {
|
|
53
|
+
process.env.PORT = saved;
|
|
54
|
+
return import("./server.js");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// First run — ask user
|
|
58
|
+
console.log(`\n\x1b[36m Claudeck\x1b[0m — first-time setup\n`);
|
|
59
|
+
const answer = await ask(` Which port would you like to use? \x1b[2m(default: ${DEFAULT_PORT})\x1b[0m `);
|
|
60
|
+
const port = answer && /^\d+$/.test(answer) ? answer : String(DEFAULT_PORT);
|
|
61
|
+
process.env.PORT = port;
|
|
62
|
+
savePort(port);
|
|
63
|
+
console.log(`\x1b[2m Saved to ~/.claudeck/.env\x1b[0m\n`);
|
|
64
|
+
return import("./server.js");
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
main();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claudeck",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A browser-based UI for Claude Code — chat, run workflows, manage MCP servers, track costs, and orchestrate autonomous agents from a local web interface. Installable as a PWA.",
|
|
6
6
|
"main": "server.js",
|
package/public/index.html
CHANGED
|
@@ -268,6 +268,11 @@
|
|
|
268
268
|
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/>
|
|
269
269
|
</svg>
|
|
270
270
|
</button>
|
|
271
|
+
<button id="remove-project-btn" class="add-project-btn sidebar-tooltip" data-tooltip="Remove project">
|
|
272
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
273
|
+
<polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>
|
|
274
|
+
</svg>
|
|
275
|
+
</button>
|
|
271
276
|
<button id="add-project-btn" class="add-project-btn sidebar-tooltip" data-tooltip="Add project">+</button>
|
|
272
277
|
<button id="system-prompt-edit-btn" class="system-prompt-edit-btn sidebar-tooltip" data-tooltip="System prompt">⚙</button>
|
|
273
278
|
</div>
|
package/public/js/core/api.js
CHANGED
|
@@ -283,6 +283,19 @@ export async function addProject(name, path) {
|
|
|
283
283
|
return res.json();
|
|
284
284
|
}
|
|
285
285
|
|
|
286
|
+
export async function deleteProject(path) {
|
|
287
|
+
const res = await fetch("/api/projects", {
|
|
288
|
+
method: "DELETE",
|
|
289
|
+
headers: { "Content-Type": "application/json" },
|
|
290
|
+
body: JSON.stringify({ path }),
|
|
291
|
+
});
|
|
292
|
+
if (!res.ok) {
|
|
293
|
+
const err = await res.json();
|
|
294
|
+
throw new Error(err.error);
|
|
295
|
+
}
|
|
296
|
+
return res.json();
|
|
297
|
+
}
|
|
298
|
+
|
|
286
299
|
export async function fetchProjectCommands(path) {
|
|
287
300
|
const res = await fetch(`/api/projects/commands?path=${encodeURIComponent(path)}`);
|
|
288
301
|
return res.json();
|
package/public/js/core/dom.js
CHANGED
|
@@ -259,6 +259,7 @@ export const $ = {
|
|
|
259
259
|
|
|
260
260
|
// Add project modal
|
|
261
261
|
openVscodeBtn: document.getElementById("open-vscode-btn"),
|
|
262
|
+
removeProjectBtn: document.getElementById("remove-project-btn"),
|
|
262
263
|
addProjectBtn: document.getElementById("add-project-btn"),
|
|
263
264
|
addProjectModal: document.getElementById("add-project-modal"),
|
|
264
265
|
addProjectClose: document.getElementById("add-project-close"),
|
|
@@ -278,6 +278,36 @@ $.openVscodeBtn.addEventListener("click", async () => {
|
|
|
278
278
|
} catch { /* ignore */ }
|
|
279
279
|
});
|
|
280
280
|
|
|
281
|
+
// Remove project
|
|
282
|
+
$.removeProjectBtn.addEventListener("click", async () => {
|
|
283
|
+
const path = $.projectSelect.value;
|
|
284
|
+
if (!path) return;
|
|
285
|
+
const name = $.projectSelect.options[$.projectSelect.selectedIndex].textContent;
|
|
286
|
+
if (!confirm(`Remove "${name}" from your projects?\n\nThis only removes it from Claudeck — your files won't be deleted.`)) return;
|
|
287
|
+
try {
|
|
288
|
+
await api.deleteProject(path);
|
|
289
|
+
// Remove from dropdown
|
|
290
|
+
const opt = [...$.projectSelect.options].find(o => o.value === path);
|
|
291
|
+
if (opt) opt.remove();
|
|
292
|
+
// Remove from state
|
|
293
|
+
const projects = getState("projectsData");
|
|
294
|
+
const idx = projects.findIndex(p => p.path === path);
|
|
295
|
+
if (idx !== -1) projects.splice(idx, 1);
|
|
296
|
+
// Reset selection
|
|
297
|
+
$.projectSelect.value = "";
|
|
298
|
+
localStorage.removeItem("claudeck-cwd");
|
|
299
|
+
updateSystemPromptIndicator();
|
|
300
|
+
updateHeaderProjectName();
|
|
301
|
+
updateSessionControls();
|
|
302
|
+
loadProjectCommands();
|
|
303
|
+
$.messagesDiv.innerHTML = "";
|
|
304
|
+
showWhalyPlaceholder();
|
|
305
|
+
loadSessions();
|
|
306
|
+
} catch (err) {
|
|
307
|
+
alert("Failed to remove project: " + err.message);
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
|
|
281
311
|
// Add project button & modal event listeners
|
|
282
312
|
$.addProjectBtn.addEventListener("click", openAddProjectModal);
|
|
283
313
|
$.addProjectClose.addEventListener("click", closeAddProjectModal);
|
package/server.js
CHANGED
|
@@ -164,7 +164,21 @@ const PORT = process.env.PORT || 9009;
|
|
|
164
164
|
// Mount full-stack plugin routes, then start server
|
|
165
165
|
mountPluginRoutes(app, fullStackPluginsDir).then(() => {
|
|
166
166
|
server.listen(PORT, () => {
|
|
167
|
-
|
|
167
|
+
const url = `http://localhost:${PORT}`;
|
|
168
|
+
console.log(`
|
|
169
|
+
\x1b[36m _____ _ _ _
|
|
170
|
+
/ ____| | | | | |
|
|
171
|
+
| | | | __ _ _ _ __| | ___ ___| | __
|
|
172
|
+
| | | |/ _\` | | | |/ _\` |/ _ \\/ __| |/ /
|
|
173
|
+
| |____| | (_| | |_| | (_| | __/ (__| <
|
|
174
|
+
\\_____|_|\\__,_|\\__,_|\\__,_|\\___|\\___|_|\\_\\\x1b[0m
|
|
175
|
+
|
|
176
|
+
\x1b[2m Browser UI for Claude Code\x1b[0m
|
|
177
|
+
|
|
178
|
+
\x1b[1m\x1b[32m➜\x1b[0m \x1b[1mReady:\x1b[0m ${url}
|
|
179
|
+
\x1b[2m➜ Port:\x1b[0m ${PORT}
|
|
180
|
+
\x1b[2m➜ Data:\x1b[0m ~/.claudeck/
|
|
181
|
+
`);
|
|
168
182
|
});
|
|
169
183
|
});
|
|
170
184
|
|