@oro.ad/nuxt-claude-devtools 1.0.6
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/LICENSE.md +674 -0
- package/README.md +176 -0
- package/dist/client/200.html +1 -0
- package/dist/client/404.html +1 -0
- package/dist/client/_nuxt/B6Pm7LNk.js +1 -0
- package/dist/client/_nuxt/BWmwj9se.js +1 -0
- package/dist/client/_nuxt/BngXb2T5.js +1 -0
- package/dist/client/_nuxt/CRwOrvc3.js +1 -0
- package/dist/client/_nuxt/DMBGVttU.js +1 -0
- package/dist/client/_nuxt/DYOOVyPh.js +1 -0
- package/dist/client/_nuxt/Dgh4EhoJ.js +1 -0
- package/dist/client/_nuxt/JxSLzYFz.js +4 -0
- package/dist/client/_nuxt/builds/latest.json +1 -0
- package/dist/client/_nuxt/builds/meta/f2b44466-6d7e-4b5c-bf6b-f6c7cf9e0d59.json +1 -0
- package/dist/client/_nuxt/entry.Ci1n7Rlt.css +1 -0
- package/dist/client/_nuxt/error-404.BLrjNXsr.css +1 -0
- package/dist/client/_nuxt/error-500.DLkAwcfL.css +1 -0
- package/dist/client/index.html +1 -0
- package/dist/client/mcp/index.html +1 -0
- package/dist/module.d.mts +14 -0
- package/dist/module.json +9 -0
- package/dist/module.mjs +81 -0
- package/dist/runtime/server/claude-session.d.ts +22 -0
- package/dist/runtime/server/claude-session.js +213 -0
- package/dist/types.d.mts +3 -0
- package/package.json +61 -0
package/dist/module.mjs
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { defineNuxtModule, createResolver } from '@nuxt/kit';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { SOCKET_PORT, getClaudeSession, destroyClaudeSession } from '../dist/runtime/server/claude-session.js';
|
|
4
|
+
|
|
5
|
+
const DEVTOOLS_UI_ROUTE = "/__claude-devtools";
|
|
6
|
+
const DEVTOOLS_UI_LOCAL_PORT = 3300;
|
|
7
|
+
function setupDevToolsUI(nuxt, resolver) {
|
|
8
|
+
const clientPath = resolver.resolve("./client");
|
|
9
|
+
const isProductionBuild = existsSync(clientPath);
|
|
10
|
+
if (isProductionBuild) {
|
|
11
|
+
nuxt.hook("vite:serverCreated", async (server) => {
|
|
12
|
+
const sirv = await import('sirv').then((r) => r.default || r);
|
|
13
|
+
server.middlewares.use(
|
|
14
|
+
DEVTOOLS_UI_ROUTE,
|
|
15
|
+
sirv(clientPath, { dev: true, single: true })
|
|
16
|
+
);
|
|
17
|
+
});
|
|
18
|
+
} else {
|
|
19
|
+
nuxt.hook("vite:extendConfig", (config) => {
|
|
20
|
+
config.server = config.server || {};
|
|
21
|
+
config.server.proxy = config.server.proxy || {};
|
|
22
|
+
config.server.proxy[DEVTOOLS_UI_ROUTE] = {
|
|
23
|
+
target: "http://localhost:" + DEVTOOLS_UI_LOCAL_PORT + DEVTOOLS_UI_ROUTE,
|
|
24
|
+
changeOrigin: true,
|
|
25
|
+
followRedirects: true,
|
|
26
|
+
rewrite: (path) => path.replace(DEVTOOLS_UI_ROUTE, "")
|
|
27
|
+
};
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
nuxt.hook("devtools:customTabs", (tabs) => {
|
|
31
|
+
tabs.push({
|
|
32
|
+
name: "claude-ai",
|
|
33
|
+
title: "AI",
|
|
34
|
+
icon: "carbon:machine-learning-model",
|
|
35
|
+
view: {
|
|
36
|
+
type: "iframe",
|
|
37
|
+
src: DEVTOOLS_UI_ROUTE
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const module$1 = defineNuxtModule({
|
|
44
|
+
meta: {
|
|
45
|
+
name: "@oro.ad/nuxt-claude-devtools",
|
|
46
|
+
configKey: "claudeDevtools"
|
|
47
|
+
},
|
|
48
|
+
defaults: {
|
|
49
|
+
enabled: true,
|
|
50
|
+
claude: {
|
|
51
|
+
command: "claude",
|
|
52
|
+
args: []
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
setup(options, nuxt) {
|
|
56
|
+
if (!nuxt.options.dev || !options.enabled) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const resolver = createResolver(import.meta.url);
|
|
60
|
+
nuxt.options.runtimeConfig.claudeDevtools = {
|
|
61
|
+
claude: options.claude,
|
|
62
|
+
rootDir: nuxt.options.rootDir
|
|
63
|
+
};
|
|
64
|
+
nuxt.hook("ready", () => {
|
|
65
|
+
console.log(`[claude-devtools] Starting Socket.IO server on port ${SOCKET_PORT}`);
|
|
66
|
+
const session = getClaudeSession({
|
|
67
|
+
command: options.claude.command,
|
|
68
|
+
args: options.claude.args,
|
|
69
|
+
rootDir: nuxt.options.rootDir
|
|
70
|
+
});
|
|
71
|
+
session.startSocketServer();
|
|
72
|
+
});
|
|
73
|
+
nuxt.hook("close", () => {
|
|
74
|
+
console.log("[claude-devtools] Cleaning up Claude session");
|
|
75
|
+
destroyClaudeSession();
|
|
76
|
+
});
|
|
77
|
+
setupDevToolsUI(nuxt, resolver);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
export { module$1 as default };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export declare const SOCKET_PORT = 3355;
|
|
2
|
+
export interface ClaudeSessionConfig {
|
|
3
|
+
command: string;
|
|
4
|
+
args: string[];
|
|
5
|
+
rootDir: string;
|
|
6
|
+
}
|
|
7
|
+
export declare class ClaudeSession {
|
|
8
|
+
private config;
|
|
9
|
+
private io;
|
|
10
|
+
private httpServer;
|
|
11
|
+
private isProcessing;
|
|
12
|
+
private continueSession;
|
|
13
|
+
constructor(config: ClaudeSessionConfig);
|
|
14
|
+
startSocketServer(): void;
|
|
15
|
+
destroy(): void;
|
|
16
|
+
private sendMessage;
|
|
17
|
+
private getMcpServers;
|
|
18
|
+
private addMcpServer;
|
|
19
|
+
private removeMcpServer;
|
|
20
|
+
}
|
|
21
|
+
export declare function getClaudeSession(config: ClaudeSessionConfig): ClaudeSession;
|
|
22
|
+
export declare function destroyClaudeSession(): void;
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { createServer } from "node:http";
|
|
2
|
+
import { execSync, spawn } from "node:child_process";
|
|
3
|
+
import { Server as SocketServer } from "socket.io";
|
|
4
|
+
function getErrorMessage(error) {
|
|
5
|
+
if (error instanceof Error) return error.message;
|
|
6
|
+
return String(error);
|
|
7
|
+
}
|
|
8
|
+
export const SOCKET_PORT = 3355;
|
|
9
|
+
function log(message, data) {
|
|
10
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
11
|
+
console.log(`[claude-session] [${timestamp}] ${message}`, data !== void 0 ? data : "");
|
|
12
|
+
}
|
|
13
|
+
export class ClaudeSession {
|
|
14
|
+
config;
|
|
15
|
+
io = null;
|
|
16
|
+
httpServer = null;
|
|
17
|
+
isProcessing = false;
|
|
18
|
+
continueSession = false;
|
|
19
|
+
constructor(config) {
|
|
20
|
+
this.config = config;
|
|
21
|
+
}
|
|
22
|
+
startSocketServer() {
|
|
23
|
+
this.httpServer = createServer();
|
|
24
|
+
this.io = new SocketServer(this.httpServer, {
|
|
25
|
+
cors: {
|
|
26
|
+
origin: "*",
|
|
27
|
+
methods: ["GET", "POST"]
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
this.io.on("connection", (socket) => {
|
|
31
|
+
log("Client connected", { socketId: socket.id });
|
|
32
|
+
socket.emit("session:status", {
|
|
33
|
+
active: true,
|
|
34
|
+
processing: this.isProcessing
|
|
35
|
+
});
|
|
36
|
+
socket.on("message:send", (message) => {
|
|
37
|
+
log("Message received", { length: message.length, preview: message.substring(0, 100) });
|
|
38
|
+
this.sendMessage(message);
|
|
39
|
+
});
|
|
40
|
+
socket.on("session:reset", () => {
|
|
41
|
+
log("Resetting session (new conversation)");
|
|
42
|
+
this.continueSession = false;
|
|
43
|
+
this.io?.emit("session:status", { active: true, processing: false });
|
|
44
|
+
});
|
|
45
|
+
socket.on("mcp:list", () => {
|
|
46
|
+
log("MCP list requested");
|
|
47
|
+
const servers = this.getMcpServers();
|
|
48
|
+
socket.emit("mcp:list", servers);
|
|
49
|
+
});
|
|
50
|
+
socket.on("mcp:add", (data) => {
|
|
51
|
+
log("MCP add requested", data);
|
|
52
|
+
this.addMcpServer(data, socket);
|
|
53
|
+
});
|
|
54
|
+
socket.on("mcp:remove", (data) => {
|
|
55
|
+
log("MCP remove requested", data);
|
|
56
|
+
this.removeMcpServer(data, socket);
|
|
57
|
+
});
|
|
58
|
+
socket.on("disconnect", () => {
|
|
59
|
+
log("Client disconnected", { socketId: socket.id });
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
this.httpServer.listen(SOCKET_PORT, () => {
|
|
63
|
+
log(`Socket.IO server started on port ${SOCKET_PORT}`);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
destroy() {
|
|
67
|
+
this.io?.close();
|
|
68
|
+
this.httpServer?.close();
|
|
69
|
+
}
|
|
70
|
+
sendMessage(message) {
|
|
71
|
+
if (this.isProcessing) {
|
|
72
|
+
log("Already processing, ignoring message");
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
this.isProcessing = true;
|
|
76
|
+
this.io?.emit("session:status", { active: true, processing: true });
|
|
77
|
+
const args = [
|
|
78
|
+
...this.config.args,
|
|
79
|
+
"-p",
|
|
80
|
+
message,
|
|
81
|
+
"--dangerously-skip-permissions"
|
|
82
|
+
];
|
|
83
|
+
if (this.continueSession) {
|
|
84
|
+
args.push("--continue");
|
|
85
|
+
}
|
|
86
|
+
log("Spawning Claude process", { command: this.config.command, args, cwd: this.config.rootDir });
|
|
87
|
+
const child = spawn(this.config.command, args, {
|
|
88
|
+
cwd: this.config.rootDir,
|
|
89
|
+
env: {
|
|
90
|
+
...process.env,
|
|
91
|
+
FORCE_COLOR: "0",
|
|
92
|
+
NO_COLOR: "1"
|
|
93
|
+
},
|
|
94
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
95
|
+
});
|
|
96
|
+
log("Claude process spawned", { pid: child.pid });
|
|
97
|
+
child.stdout?.on("data", (data) => {
|
|
98
|
+
const chunk = data.toString();
|
|
99
|
+
log("stdout chunk", { length: chunk.length });
|
|
100
|
+
this.io?.emit("output:chunk", chunk);
|
|
101
|
+
});
|
|
102
|
+
child.stderr?.on("data", (data) => {
|
|
103
|
+
const chunk = data.toString();
|
|
104
|
+
log("stderr chunk", { length: chunk.length, preview: chunk.substring(0, 200) });
|
|
105
|
+
});
|
|
106
|
+
child.on("error", (error) => {
|
|
107
|
+
log("Process error", { error: error.message });
|
|
108
|
+
this.io?.emit("session:error", error.message);
|
|
109
|
+
this.isProcessing = false;
|
|
110
|
+
this.io?.emit("session:status", { active: true, processing: false });
|
|
111
|
+
});
|
|
112
|
+
child.on("close", (code) => {
|
|
113
|
+
log("Process closed", { exitCode: code });
|
|
114
|
+
this.isProcessing = false;
|
|
115
|
+
if (code === 0) {
|
|
116
|
+
this.continueSession = true;
|
|
117
|
+
this.io?.emit("output:complete");
|
|
118
|
+
} else {
|
|
119
|
+
this.io?.emit("session:error", `Process exited with code ${code}`);
|
|
120
|
+
}
|
|
121
|
+
this.io?.emit("session:status", { active: true, processing: false });
|
|
122
|
+
});
|
|
123
|
+
child.stdin?.end();
|
|
124
|
+
}
|
|
125
|
+
getMcpServers() {
|
|
126
|
+
const servers = [];
|
|
127
|
+
try {
|
|
128
|
+
const output = execSync(`${this.config.command} mcp list`, {
|
|
129
|
+
cwd: this.config.rootDir,
|
|
130
|
+
encoding: "utf-8",
|
|
131
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
132
|
+
});
|
|
133
|
+
log("MCP list output", { output });
|
|
134
|
+
const lines = output.split("\n").filter((line) => line.trim() && !line.includes("Checking"));
|
|
135
|
+
for (const line of lines) {
|
|
136
|
+
const match = line.match(/^(\S+):\s(\S+(?:\s\S+)*)\s\((\w+)\)/);
|
|
137
|
+
if (match) {
|
|
138
|
+
const [, name, urlOrCommand, type] = match;
|
|
139
|
+
const transport = type.toLowerCase();
|
|
140
|
+
servers.push({
|
|
141
|
+
name,
|
|
142
|
+
url: transport !== "stdio" ? urlOrCommand : void 0,
|
|
143
|
+
command: transport === "stdio" ? urlOrCommand.split(" ")[0] : void 0,
|
|
144
|
+
args: transport === "stdio" ? urlOrCommand.split(" ").slice(1) : [],
|
|
145
|
+
transport,
|
|
146
|
+
scope: "local"
|
|
147
|
+
// We'll determine scope separately if needed
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
} catch (error) {
|
|
152
|
+
log("Error getting MCP servers", { error: getErrorMessage(error) });
|
|
153
|
+
}
|
|
154
|
+
log("MCP servers found", { count: servers.length });
|
|
155
|
+
return servers;
|
|
156
|
+
}
|
|
157
|
+
addMcpServer(data, socket) {
|
|
158
|
+
const scopeArg = data.scope === "global" ? "--scope user" : "--scope local";
|
|
159
|
+
let cmd;
|
|
160
|
+
if (data.transport === "stdio") {
|
|
161
|
+
const argsStr = (data.args || []).join(" ");
|
|
162
|
+
cmd = `${this.config.command} mcp add ${scopeArg} ${data.name} -- ${data.command} ${argsStr}`;
|
|
163
|
+
} else {
|
|
164
|
+
cmd = `${this.config.command} mcp add --transport ${data.transport} ${scopeArg} ${data.name} ${data.url}`;
|
|
165
|
+
}
|
|
166
|
+
log("Running MCP add command", { cmd });
|
|
167
|
+
try {
|
|
168
|
+
execSync(cmd, {
|
|
169
|
+
cwd: this.config.rootDir,
|
|
170
|
+
encoding: "utf-8",
|
|
171
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
172
|
+
});
|
|
173
|
+
socket.emit("mcp:added", { success: true, name: data.name });
|
|
174
|
+
const servers = this.getMcpServers();
|
|
175
|
+
socket.emit("mcp:list", servers);
|
|
176
|
+
} catch (error) {
|
|
177
|
+
const errorMessage = getErrorMessage(error);
|
|
178
|
+
log("Error adding MCP server", { error: errorMessage });
|
|
179
|
+
socket.emit("mcp:added", { success: false, error: errorMessage });
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
removeMcpServer(data, socket) {
|
|
183
|
+
const cmd = `${this.config.command} mcp remove ${data.name}`;
|
|
184
|
+
log("Running MCP remove command", { cmd });
|
|
185
|
+
try {
|
|
186
|
+
execSync(cmd, {
|
|
187
|
+
cwd: this.config.rootDir,
|
|
188
|
+
encoding: "utf-8",
|
|
189
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
190
|
+
});
|
|
191
|
+
socket.emit("mcp:removed", { success: true, name: data.name });
|
|
192
|
+
const servers = this.getMcpServers();
|
|
193
|
+
socket.emit("mcp:list", servers);
|
|
194
|
+
} catch (error) {
|
|
195
|
+
const errorMessage = getErrorMessage(error);
|
|
196
|
+
log("Error removing MCP server", { error: errorMessage });
|
|
197
|
+
socket.emit("mcp:removed", { success: false, error: errorMessage });
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
let sessionInstance = null;
|
|
202
|
+
export function getClaudeSession(config) {
|
|
203
|
+
if (!sessionInstance) {
|
|
204
|
+
sessionInstance = new ClaudeSession(config);
|
|
205
|
+
}
|
|
206
|
+
return sessionInstance;
|
|
207
|
+
}
|
|
208
|
+
export function destroyClaudeSession() {
|
|
209
|
+
if (sessionInstance) {
|
|
210
|
+
sessionInstance.destroy();
|
|
211
|
+
sessionInstance = null;
|
|
212
|
+
}
|
|
213
|
+
}
|
package/dist/types.d.mts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@oro.ad/nuxt-claude-devtools",
|
|
3
|
+
"version": "1.0.6",
|
|
4
|
+
"description": "Nuxt DevTools integration for Claude Code AI assistant",
|
|
5
|
+
"license": "GPL-3.0-only",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/types.d.mts",
|
|
10
|
+
"import": "./dist/module.mjs"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"repository": {
|
|
14
|
+
"url": "https://github.com/oro-ad/nuxt-claude-devtools"
|
|
15
|
+
},
|
|
16
|
+
"main": "./dist/module.mjs",
|
|
17
|
+
"author": "Simon Bystrov <jobsbystr@gmail.com> (https://sbystr.com/)",
|
|
18
|
+
"typesVersions": {
|
|
19
|
+
"*": {
|
|
20
|
+
".": [
|
|
21
|
+
"./dist/types.d.mts"
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist"
|
|
27
|
+
],
|
|
28
|
+
"scripts": {
|
|
29
|
+
"prepack": "nuxt-module-build && npm run client:build",
|
|
30
|
+
"client:build": "nuxi generate client",
|
|
31
|
+
"client:dev": "nuxi dev client --port 3300",
|
|
32
|
+
"dev": "npm run play:dev",
|
|
33
|
+
"dev:prepare": "nuxt-module-build --stub && nuxi prepare client",
|
|
34
|
+
"prepare": "nuxi prepare client",
|
|
35
|
+
"play:dev": "nuxi dev playground",
|
|
36
|
+
"play:prod": "npm run prepack && nuxi dev playground",
|
|
37
|
+
"lint": "eslint ."
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@nuxt/devtools-kit": "^3.1.1",
|
|
41
|
+
"@nuxt/kit": "^4.2.2",
|
|
42
|
+
"sirv": "^3.0.2",
|
|
43
|
+
"socket.io": "^4.8.1"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@iconify-json/carbon": "^1.2.15",
|
|
47
|
+
"@nuxt/devtools": "^3.1.1",
|
|
48
|
+
"@nuxt/devtools-ui-kit": "^3.1.1",
|
|
49
|
+
"@nuxt/eslint-config": "^1.12.1",
|
|
50
|
+
"@nuxt/module-builder": "^1.0.2",
|
|
51
|
+
"@nuxt/schema": "^4.2.2",
|
|
52
|
+
"@nuxt/ui": "^4.3.0",
|
|
53
|
+
"@nuxtjs/i18n": "^10.2.1",
|
|
54
|
+
"changelogen": "^0.6.2",
|
|
55
|
+
"eslint": "^9.39.2",
|
|
56
|
+
"nuxt": "^4.2.2"
|
|
57
|
+
},
|
|
58
|
+
"publishConfig": {
|
|
59
|
+
"access": "public"
|
|
60
|
+
}
|
|
61
|
+
}
|