@oro.ad/nuxt-claude-devtools 1.2.0 → 1.3.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/README.md +105 -13
- package/dist/client/200.html +1 -1
- package/dist/client/404.html +1 -1
- package/dist/client/_nuxt/B8uzckkK.js +1 -0
- package/dist/client/_nuxt/{CPQSDiF7.js → BAb1fJOF.js} +4 -4
- package/dist/client/_nuxt/BB1-kxmm.js +1 -0
- package/dist/client/_nuxt/BMZIbUUD.js +1 -0
- package/dist/client/_nuxt/BSF2Vz9o.js +1 -0
- package/dist/client/_nuxt/BSVkH7b6.js +1 -0
- package/dist/client/_nuxt/{08Mb3FOO.js → BYp73eMl.js} +1 -1
- package/dist/client/_nuxt/B_BoWmnX.js +1 -0
- package/dist/client/_nuxt/BcZxFXBD.js +1 -0
- package/dist/client/_nuxt/BflmC3YB.js +1 -0
- package/dist/client/_nuxt/BnXQTjo-.js +1 -0
- package/dist/client/_nuxt/C--9REmc.js +1 -0
- package/dist/client/_nuxt/CDQtmRaX.js +1 -0
- package/dist/client/_nuxt/CHeJJZL9.js +1 -0
- package/dist/client/_nuxt/{CSlPuO5s.js → COus5Ssl.js} +1 -1
- package/dist/client/_nuxt/{o1jjB-UO.js → CPA0s6N9.js} +1 -1
- package/dist/client/_nuxt/CRkq21kc.js +1 -0
- package/dist/client/_nuxt/Cgba93Y9.js +1 -0
- package/dist/client/_nuxt/D2l4TRxW.js +1 -0
- package/dist/client/_nuxt/DC_XB519.js +1 -0
- package/dist/client/_nuxt/DEys9N1G.js +1 -0
- package/dist/client/_nuxt/{b4Upel01.js → DGQ4s7ae.js} +1 -1
- package/dist/client/_nuxt/DH8Ugy8E.js +1 -0
- package/dist/client/_nuxt/DSt96JPY.js +4 -0
- package/dist/client/_nuxt/DbJLoP3G.js +1 -0
- package/dist/client/_nuxt/DeGmaFBY.js +1 -0
- package/dist/client/_nuxt/DgfRwrFR.js +1 -0
- package/dist/client/_nuxt/{CLKqRoht.js → DolUcBed.js} +1 -1
- package/dist/client/_nuxt/M6QPYocW.js +1 -0
- package/dist/client/_nuxt/QumocfwJ.js +1 -0
- package/dist/client/_nuxt/TQi6eIO6.js +1 -0
- package/dist/client/_nuxt/V4UvAcd3.js +1 -0
- package/dist/client/_nuxt/builds/latest.json +1 -1
- package/dist/client/_nuxt/builds/meta/2be12f06-336a-4fdd-b982-2f6c682c14a6.json +1 -0
- package/dist/client/_nuxt/d8BPa19J.js +1 -0
- package/dist/client/_nuxt/entry.BMxUr06A.css +1 -0
- package/dist/client/_nuxt/qbS8UemQ.js +1 -0
- package/dist/client/_nuxt/wDw60tEC.js +10 -0
- package/dist/client/_nuxt/xEjB6ozD.js +1 -0
- package/dist/client/agents/index.html +1 -1
- package/dist/client/commands/index.html +1 -1
- package/dist/client/docs/index.html +1 -1
- package/dist/client/index.html +1 -1
- package/dist/client/mcp/index.html +1 -1
- package/dist/client/plugins/index.html +1 -0
- package/dist/client/settings/index.html +1 -0
- package/dist/client/skills/index.html +1 -1
- package/dist/module.d.mts +12 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +27 -5
- package/dist/runtime/constants.d.ts +29 -0
- package/dist/runtime/constants.js +5 -0
- package/dist/runtime/overlay/components/ChatOverlay.d.vue.ts +7 -0
- package/dist/runtime/overlay/components/ChatOverlay.vue +893 -0
- package/dist/runtime/overlay/components/ChatOverlay.vue.d.ts +7 -0
- package/dist/runtime/overlay/components/MarkdownContent.d.vue.ts +6 -0
- package/dist/runtime/overlay/components/MarkdownContent.vue +31 -0
- package/dist/runtime/overlay/components/MarkdownContent.vue.d.ts +6 -0
- package/dist/runtime/overlay/components/ToolCallBlock.d.vue.ts +8 -0
- package/dist/runtime/overlay/components/ToolCallBlock.vue +77 -0
- package/dist/runtime/overlay/components/ToolCallBlock.vue.d.ts +8 -0
- package/dist/runtime/overlay/plugin.client.d.ts +6 -0
- package/dist/runtime/overlay/plugin.client.js +29 -0
- package/dist/runtime/server/agents-manager.d.ts +17 -4
- package/dist/runtime/server/agents-manager.js +38 -109
- package/dist/runtime/server/base-resource-manager.d.ts +90 -0
- package/dist/runtime/server/base-resource-manager.js +201 -0
- package/dist/runtime/server/claude-session.d.ts +11 -1
- package/dist/runtime/server/claude-session.js +246 -27
- package/dist/runtime/server/commands-manager.d.ts +12 -4
- package/dist/runtime/server/commands-manager.js +25 -100
- package/dist/runtime/server/constants.d.ts +94 -0
- package/dist/runtime/server/constants.js +18 -0
- package/dist/runtime/server/docs-manager.d.ts +7 -0
- package/dist/runtime/server/docs-manager.js +112 -3
- package/dist/runtime/server/history-manager.d.ts +1 -0
- package/dist/runtime/server/history-manager.js +25 -3
- package/dist/runtime/server/plugins/socket.io.js +5 -3
- package/dist/runtime/server/plugins-manager.d.ts +84 -0
- package/dist/runtime/server/plugins-manager.js +338 -0
- package/dist/runtime/server/settings-manager.d.ts +17 -0
- package/dist/runtime/server/settings-manager.js +70 -0
- package/dist/runtime/server/share-manager.d.ts +24 -0
- package/dist/runtime/server/share-manager.js +96 -0
- package/dist/runtime/server/skills-manager.d.ts +18 -4
- package/dist/runtime/server/skills-manager.js +32 -159
- package/dist/runtime/shared/composables/useClaudeChat.d.ts +35 -0
- package/dist/runtime/shared/composables/useClaudeChat.js +264 -0
- package/dist/runtime/shared/composables/useShare.d.ts +42 -0
- package/dist/runtime/shared/composables/useShare.js +192 -0
- package/dist/runtime/shared/composables/useVoiceInput.d.ts +8 -0
- package/dist/runtime/shared/composables/useVoiceInput.js +77 -0
- package/dist/runtime/shared/constants.d.ts +28 -0
- package/dist/runtime/shared/constants.js +6 -0
- package/dist/runtime/shared/index.d.ts +9 -0
- package/dist/runtime/shared/index.js +5 -0
- package/dist/runtime/shared/types.d.ts +48 -0
- package/dist/runtime/shared/types.js +0 -0
- package/dist/runtime/types.d.ts +2 -0
- package/dist/types.d.mts +1 -1
- package/package.json +5 -3
- package/dist/client/_nuxt/BMi2eT6G.js +0 -1
- package/dist/client/_nuxt/BiBLVxWh.js +0 -1
- package/dist/client/_nuxt/BnRGpZsC.js +0 -8
- package/dist/client/_nuxt/C2GhPw5d.js +0 -7
- package/dist/client/_nuxt/D8683igF.js +0 -7
- package/dist/client/_nuxt/DBIw6BGF.js +0 -1
- package/dist/client/_nuxt/DCgjfr8H.js +0 -9
- package/dist/client/_nuxt/bl5iU4Kz.js +0 -1
- package/dist/client/_nuxt/builds/meta/35284331-5e85-46e0-9058-988fea05336c.json +0 -1
- package/dist/client/_nuxt/entry.BhHeZ_Nj.css +0 -1
- package/dist/client/_nuxt/nKfsBgPE.js +0 -4
|
@@ -1,134 +1,28 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
3
1
|
import { createLogger } from "../logger.js";
|
|
2
|
+
import { MarkdownResourceManager } from "./base-resource-manager.js";
|
|
3
|
+
import { SKILL_FILE, SKILLS_SUBDIR } from "./constants.js";
|
|
4
4
|
const log = createLogger("skills", { timestamp: true });
|
|
5
|
-
export class SkillsManager {
|
|
6
|
-
skillsDir;
|
|
5
|
+
export class SkillsManager extends MarkdownResourceManager {
|
|
7
6
|
constructor(projectPath) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
// Parse YAML frontmatter from markdown content
|
|
15
|
-
parseFrontmatter(content) {
|
|
16
|
-
const frontmatterRegex = /^---\n([\s\S]*?)\n---\n([\s\S]*)$/;
|
|
17
|
-
const match = content.match(frontmatterRegex);
|
|
18
|
-
if (!match) {
|
|
19
|
-
return { frontmatter: {}, body: content.trim() };
|
|
20
|
-
}
|
|
21
|
-
const [, yaml, body] = match;
|
|
22
|
-
const frontmatter = {};
|
|
23
|
-
for (const line of yaml.split("\n")) {
|
|
24
|
-
const colonIndex = line.indexOf(":");
|
|
25
|
-
if (colonIndex === -1) continue;
|
|
26
|
-
const key = line.slice(0, colonIndex).trim();
|
|
27
|
-
const value = line.slice(colonIndex + 1).trim();
|
|
28
|
-
switch (key) {
|
|
29
|
-
case "name":
|
|
30
|
-
frontmatter.name = value;
|
|
31
|
-
break;
|
|
32
|
-
case "description":
|
|
33
|
-
frontmatter.description = value;
|
|
34
|
-
break;
|
|
35
|
-
case "argument-hint":
|
|
36
|
-
frontmatter["argument-hint"] = value;
|
|
37
|
-
break;
|
|
38
|
-
case "disable-model-invocation":
|
|
39
|
-
frontmatter["disable-model-invocation"] = value === "true";
|
|
40
|
-
break;
|
|
41
|
-
case "user-invocable":
|
|
42
|
-
frontmatter["user-invocable"] = value === "true";
|
|
43
|
-
break;
|
|
44
|
-
case "allowed-tools":
|
|
45
|
-
frontmatter["allowed-tools"] = value;
|
|
46
|
-
break;
|
|
47
|
-
case "model":
|
|
48
|
-
frontmatter.model = value;
|
|
49
|
-
break;
|
|
50
|
-
case "context":
|
|
51
|
-
frontmatter.context = value;
|
|
52
|
-
break;
|
|
53
|
-
case "agent":
|
|
54
|
-
frontmatter.agent = value;
|
|
55
|
-
break;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
return { frontmatter, body: body.trim() };
|
|
7
|
+
super(projectPath, SKILLS_SUBDIR, log, {
|
|
8
|
+
useSubdirectories: true,
|
|
9
|
+
subdirectoryFilename: SKILL_FILE
|
|
10
|
+
});
|
|
59
11
|
}
|
|
60
|
-
// Build frontmatter string
|
|
61
12
|
buildFrontmatter(skill) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
lines.push(`disable-model-invocation: ${skill.disableModelInvocation}`);
|
|
74
|
-
}
|
|
75
|
-
if (skill.userInvocable !== void 0) {
|
|
76
|
-
lines.push(`user-invocable: ${skill.userInvocable}`);
|
|
77
|
-
}
|
|
78
|
-
if (skill.allowedTools && skill.allowedTools.length > 0) {
|
|
79
|
-
lines.push(`allowed-tools: ${skill.allowedTools.join(", ")}`);
|
|
80
|
-
}
|
|
81
|
-
if (skill.model) {
|
|
82
|
-
lines.push(`model: ${skill.model}`);
|
|
83
|
-
}
|
|
84
|
-
if (skill.context) {
|
|
85
|
-
lines.push(`context: ${skill.context}`);
|
|
86
|
-
}
|
|
87
|
-
if (skill.agent) {
|
|
88
|
-
lines.push(`agent: ${skill.agent}`);
|
|
89
|
-
}
|
|
90
|
-
lines.push("---");
|
|
91
|
-
return lines.join("\n");
|
|
13
|
+
return this.buildFrontmatterLines([
|
|
14
|
+
{ key: "name", value: skill.name },
|
|
15
|
+
{ key: "description", value: skill.description },
|
|
16
|
+
{ key: "argument-hint", value: skill.argumentHint },
|
|
17
|
+
{ key: "disable-model-invocation", value: skill.disableModelInvocation },
|
|
18
|
+
{ key: "user-invocable", value: skill.userInvocable },
|
|
19
|
+
{ key: "allowed-tools", value: skill.allowedTools },
|
|
20
|
+
{ key: "model", value: skill.model },
|
|
21
|
+
{ key: "context", value: skill.context },
|
|
22
|
+
{ key: "agent", value: skill.agent }
|
|
23
|
+
]);
|
|
92
24
|
}
|
|
93
|
-
|
|
94
|
-
getSkills() {
|
|
95
|
-
const skills = [];
|
|
96
|
-
if (!existsSync(this.skillsDir)) return skills;
|
|
97
|
-
const entries = readdirSync(this.skillsDir);
|
|
98
|
-
for (const entry of entries) {
|
|
99
|
-
const skillDir = join(this.skillsDir, entry);
|
|
100
|
-
const stat = statSync(skillDir);
|
|
101
|
-
if (!stat.isDirectory()) continue;
|
|
102
|
-
const skillFile = join(skillDir, "SKILL.md");
|
|
103
|
-
if (!existsSync(skillFile)) continue;
|
|
104
|
-
const fileStat = statSync(skillFile);
|
|
105
|
-
const rawContent = readFileSync(skillFile, "utf-8");
|
|
106
|
-
const { frontmatter, body } = this.parseFrontmatter(rawContent);
|
|
107
|
-
skills.push({
|
|
108
|
-
name: frontmatter.name || entry,
|
|
109
|
-
description: frontmatter.description || "",
|
|
110
|
-
content: body,
|
|
111
|
-
rawContent,
|
|
112
|
-
argumentHint: frontmatter["argument-hint"],
|
|
113
|
-
disableModelInvocation: frontmatter["disable-model-invocation"],
|
|
114
|
-
userInvocable: frontmatter["user-invocable"],
|
|
115
|
-
allowedTools: frontmatter["allowed-tools"] ? frontmatter["allowed-tools"].split(",").map((s) => s.trim()) : void 0,
|
|
116
|
-
model: frontmatter.model,
|
|
117
|
-
context: frontmatter.context === "fork" ? "fork" : void 0,
|
|
118
|
-
agent: frontmatter.agent,
|
|
119
|
-
updatedAt: fileStat.mtime.toISOString()
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
return skills.sort((a, b) => a.name.localeCompare(b.name));
|
|
123
|
-
}
|
|
124
|
-
// Get single skill
|
|
125
|
-
getSkill(name) {
|
|
126
|
-
const skillDir = join(this.skillsDir, name);
|
|
127
|
-
const skillFile = join(skillDir, "SKILL.md");
|
|
128
|
-
if (!existsSync(skillFile)) return null;
|
|
129
|
-
const stat = statSync(skillFile);
|
|
130
|
-
const rawContent = readFileSync(skillFile, "utf-8");
|
|
131
|
-
const { frontmatter, body } = this.parseFrontmatter(rawContent);
|
|
25
|
+
toResource(name, frontmatter, body, rawContent, updatedAt) {
|
|
132
26
|
return {
|
|
133
27
|
name: frontmatter.name || name,
|
|
134
28
|
description: frontmatter.description || "",
|
|
@@ -141,33 +35,21 @@ export class SkillsManager {
|
|
|
141
35
|
model: frontmatter.model,
|
|
142
36
|
context: frontmatter.context === "fork" ? "fork" : void 0,
|
|
143
37
|
agent: frontmatter.agent,
|
|
144
|
-
updatedAt
|
|
38
|
+
updatedAt
|
|
145
39
|
};
|
|
146
40
|
}
|
|
147
|
-
//
|
|
41
|
+
// Public API methods
|
|
42
|
+
getSkills() {
|
|
43
|
+
return this.getAll();
|
|
44
|
+
}
|
|
45
|
+
getSkill(name) {
|
|
46
|
+
return this.getOne(name);
|
|
47
|
+
}
|
|
148
48
|
getSkillNames() {
|
|
149
|
-
|
|
150
|
-
const names = [];
|
|
151
|
-
const entries = readdirSync(this.skillsDir);
|
|
152
|
-
for (const entry of entries) {
|
|
153
|
-
const skillDir = join(this.skillsDir, entry);
|
|
154
|
-
const stat = statSync(skillDir);
|
|
155
|
-
if (!stat.isDirectory()) continue;
|
|
156
|
-
const skillFile = join(skillDir, "SKILL.md");
|
|
157
|
-
if (existsSync(skillFile)) {
|
|
158
|
-
names.push(entry);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
return names.sort();
|
|
49
|
+
return this.getAll().map((s) => s.name).sort();
|
|
162
50
|
}
|
|
163
|
-
// Create or update skill
|
|
164
51
|
saveSkill(skill) {
|
|
165
|
-
const safeName = skill.name
|
|
166
|
-
const skillDir = join(this.skillsDir, safeName);
|
|
167
|
-
const skillFile = join(skillDir, "SKILL.md");
|
|
168
|
-
if (!existsSync(skillDir)) {
|
|
169
|
-
mkdirSync(skillDir, { recursive: true });
|
|
170
|
-
}
|
|
52
|
+
const safeName = this.sanitizeName(skill.name);
|
|
171
53
|
const frontmatter = this.buildFrontmatter({
|
|
172
54
|
name: safeName,
|
|
173
55
|
description: skill.description,
|
|
@@ -179,11 +61,7 @@ export class SkillsManager {
|
|
|
179
61
|
context: skill.context,
|
|
180
62
|
agent: skill.agent
|
|
181
63
|
});
|
|
182
|
-
const rawContent =
|
|
183
|
-
|
|
184
|
-
${skill.content}`;
|
|
185
|
-
writeFileSync(skillFile, rawContent, "utf-8");
|
|
186
|
-
log("Saved skill", { name: safeName, path: skillFile });
|
|
64
|
+
const { rawContent, updatedAt } = this.saveResource(safeName, frontmatter, skill.content);
|
|
187
65
|
return {
|
|
188
66
|
name: safeName,
|
|
189
67
|
description: skill.description,
|
|
@@ -196,15 +74,10 @@ ${skill.content}`;
|
|
|
196
74
|
model: skill.model,
|
|
197
75
|
context: skill.context,
|
|
198
76
|
agent: skill.agent,
|
|
199
|
-
updatedAt
|
|
77
|
+
updatedAt
|
|
200
78
|
};
|
|
201
79
|
}
|
|
202
|
-
// Delete skill
|
|
203
80
|
deleteSkill(name) {
|
|
204
|
-
|
|
205
|
-
if (!existsSync(skillDir)) return false;
|
|
206
|
-
rmSync(skillDir, { recursive: true });
|
|
207
|
-
log("Deleted skill", { name });
|
|
208
|
-
return true;
|
|
81
|
+
return this.delete(name);
|
|
209
82
|
}
|
|
210
83
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { ComputedRef, Ref } from 'vue';
|
|
2
|
+
import type { Socket } from 'socket.io-client';
|
|
3
|
+
import type { ContentBlock, Conversation, DocFile, Message, SlashCommand } from '../types.js';
|
|
4
|
+
interface UseChatOptions {
|
|
5
|
+
onDocsReceived?: (docs: DocFile[]) => void;
|
|
6
|
+
onCommandsReceived?: (commands: SlashCommand[]) => void;
|
|
7
|
+
log?: (...args: unknown[]) => void;
|
|
8
|
+
/** Custom socket URL (for tunnel support) */
|
|
9
|
+
socketUrl?: string;
|
|
10
|
+
/** Current user ID for collaborative mode */
|
|
11
|
+
getCurrentUserId?: () => string | null;
|
|
12
|
+
}
|
|
13
|
+
/** Return type for useClaudeChat composable */
|
|
14
|
+
export interface UseChatReturn {
|
|
15
|
+
socket: Ref<Socket | null>;
|
|
16
|
+
messages: Ref<Message[]>;
|
|
17
|
+
conversations: Ref<Conversation[]>;
|
|
18
|
+
activeConversationId: Ref<string | null>;
|
|
19
|
+
isConnected: Ref<boolean>;
|
|
20
|
+
isSessionActive: Ref<boolean>;
|
|
21
|
+
isProcessing: Ref<boolean>;
|
|
22
|
+
isHistoryOpen: Ref<boolean>;
|
|
23
|
+
statusText: ComputedRef<string>;
|
|
24
|
+
statusColor: ComputedRef<string>;
|
|
25
|
+
connectSocket: () => void;
|
|
26
|
+
disconnect: () => void;
|
|
27
|
+
newChat: () => void;
|
|
28
|
+
sendMessage: (content: string, senderId?: string, senderNickname?: string) => void;
|
|
29
|
+
addMessage: (role: Message['role'], content: string, streaming?: boolean) => Message;
|
|
30
|
+
toggleHistory: () => void;
|
|
31
|
+
selectConversation: (id: string) => void;
|
|
32
|
+
findToolResult: (blocks: ContentBlock[] | undefined, toolUseId: string) => ContentBlock | undefined;
|
|
33
|
+
}
|
|
34
|
+
export declare function useClaudeChat(options?: UseChatOptions): UseChatReturn;
|
|
35
|
+
export {};
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { computed, ref } from "vue";
|
|
2
|
+
import { io } from "socket.io-client";
|
|
3
|
+
import { SOCKET_PATH } from "../constants.js";
|
|
4
|
+
export function useClaudeChat(options = {}) {
|
|
5
|
+
const { log = () => {
|
|
6
|
+
}, onDocsReceived, onCommandsReceived, socketUrl, getCurrentUserId } = options;
|
|
7
|
+
const socket = ref(null);
|
|
8
|
+
const messages = ref([]);
|
|
9
|
+
const conversations = ref([]);
|
|
10
|
+
const activeConversationId = ref(null);
|
|
11
|
+
const isConnected = ref(false);
|
|
12
|
+
const isSessionActive = ref(false);
|
|
13
|
+
const isProcessing = ref(false);
|
|
14
|
+
const isHistoryOpen = ref(false);
|
|
15
|
+
const pendingToolCalls = ref(/* @__PURE__ */ new Map());
|
|
16
|
+
const statusText = computed(() => {
|
|
17
|
+
if (!isConnected.value) return "Disconnected";
|
|
18
|
+
if (isProcessing.value) return "Processing...";
|
|
19
|
+
return "Ready";
|
|
20
|
+
});
|
|
21
|
+
const statusColor = computed(() => {
|
|
22
|
+
if (!isConnected.value) return "red";
|
|
23
|
+
if (isProcessing.value) return "blue";
|
|
24
|
+
return "green";
|
|
25
|
+
});
|
|
26
|
+
function generateId() {
|
|
27
|
+
return Math.random().toString(36).substring(2, 9);
|
|
28
|
+
}
|
|
29
|
+
function addMessage(role, content, streaming = false) {
|
|
30
|
+
const message = {
|
|
31
|
+
id: generateId(),
|
|
32
|
+
role,
|
|
33
|
+
content,
|
|
34
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
35
|
+
streaming
|
|
36
|
+
};
|
|
37
|
+
messages.value.push(message);
|
|
38
|
+
return message;
|
|
39
|
+
}
|
|
40
|
+
function getSocketUrl() {
|
|
41
|
+
if (socketUrl) return socketUrl;
|
|
42
|
+
if (typeof window !== "undefined") {
|
|
43
|
+
return window.location.origin;
|
|
44
|
+
}
|
|
45
|
+
return "";
|
|
46
|
+
}
|
|
47
|
+
function connectSocket() {
|
|
48
|
+
const url = getSocketUrl();
|
|
49
|
+
log("Connecting to socket at", url);
|
|
50
|
+
socket.value = io(url, {
|
|
51
|
+
path: SOCKET_PATH,
|
|
52
|
+
transports: ["websocket", "polling"],
|
|
53
|
+
reconnection: true,
|
|
54
|
+
reconnectionAttempts: 5
|
|
55
|
+
});
|
|
56
|
+
socket.value.on("connect", () => {
|
|
57
|
+
log("Connected to socket");
|
|
58
|
+
isConnected.value = true;
|
|
59
|
+
socket.value?.emit("docs:list");
|
|
60
|
+
socket.value?.emit("commands:list");
|
|
61
|
+
});
|
|
62
|
+
socket.value.on("disconnect", () => {
|
|
63
|
+
log("Disconnected from socket");
|
|
64
|
+
isConnected.value = false;
|
|
65
|
+
isSessionActive.value = false;
|
|
66
|
+
isProcessing.value = false;
|
|
67
|
+
});
|
|
68
|
+
socket.value.on("session:status", (status) => {
|
|
69
|
+
log("Session status:", status);
|
|
70
|
+
isSessionActive.value = status.active;
|
|
71
|
+
isProcessing.value = status.processing;
|
|
72
|
+
});
|
|
73
|
+
socket.value.on("history:loaded", (conversation) => {
|
|
74
|
+
log("History loaded:", conversation.id);
|
|
75
|
+
activeConversationId.value = conversation.id;
|
|
76
|
+
messages.value = conversation.messages.map((m) => ({
|
|
77
|
+
...m,
|
|
78
|
+
timestamp: new Date(m.timestamp)
|
|
79
|
+
}));
|
|
80
|
+
});
|
|
81
|
+
socket.value.on("history:list", (convs) => {
|
|
82
|
+
log("Conversations list:", convs.length);
|
|
83
|
+
conversations.value = convs;
|
|
84
|
+
});
|
|
85
|
+
socket.value.on("history:switched", (conversation) => {
|
|
86
|
+
log("Switched to conversation:", conversation.id);
|
|
87
|
+
activeConversationId.value = conversation.id;
|
|
88
|
+
messages.value = conversation.messages.map((m) => ({
|
|
89
|
+
...m,
|
|
90
|
+
timestamp: new Date(m.timestamp)
|
|
91
|
+
}));
|
|
92
|
+
isHistoryOpen.value = false;
|
|
93
|
+
});
|
|
94
|
+
socket.value.on("docs:list", (files) => {
|
|
95
|
+
log("Docs list received:", files.length);
|
|
96
|
+
onDocsReceived?.(files);
|
|
97
|
+
});
|
|
98
|
+
socket.value.on("commands:list", (cmds) => {
|
|
99
|
+
log("Commands list received:", cmds.length);
|
|
100
|
+
onCommandsReceived?.(cmds);
|
|
101
|
+
});
|
|
102
|
+
socket.value.on("stream:message_start", (data) => {
|
|
103
|
+
log("Message start:", data.id);
|
|
104
|
+
pendingToolCalls.value.clear();
|
|
105
|
+
const lastMessage = messages.value[messages.value.length - 1];
|
|
106
|
+
if (!lastMessage || lastMessage.role !== "assistant" || !lastMessage.streaming) {
|
|
107
|
+
addMessage("assistant", "", true);
|
|
108
|
+
}
|
|
109
|
+
const streamingMessage = messages.value.findLast((m) => m.role === "assistant" && m.streaming);
|
|
110
|
+
if (streamingMessage && !streamingMessage.contentBlocks) {
|
|
111
|
+
streamingMessage.contentBlocks = [];
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
socket.value.on("stream:tool_use", (data) => {
|
|
115
|
+
log("Tool use:", data.name);
|
|
116
|
+
const toolBlock = {
|
|
117
|
+
type: "tool_use",
|
|
118
|
+
id: data.id,
|
|
119
|
+
name: data.name,
|
|
120
|
+
input: data.input
|
|
121
|
+
};
|
|
122
|
+
pendingToolCalls.value.set(data.id, toolBlock);
|
|
123
|
+
const lastMessage = messages.value.findLast((m) => m.role === "assistant" && m.streaming) || messages.value.findLast((m) => m.role === "assistant");
|
|
124
|
+
if (lastMessage) {
|
|
125
|
+
if (!lastMessage.contentBlocks) {
|
|
126
|
+
lastMessage.contentBlocks = [];
|
|
127
|
+
}
|
|
128
|
+
lastMessage.contentBlocks.push(toolBlock);
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
socket.value.on("stream:tool_result", (data) => {
|
|
132
|
+
log("Tool result:", data.tool_use_id);
|
|
133
|
+
const resultBlock = {
|
|
134
|
+
type: "tool_result",
|
|
135
|
+
tool_use_id: data.tool_use_id,
|
|
136
|
+
content: data.content,
|
|
137
|
+
is_error: data.is_error
|
|
138
|
+
};
|
|
139
|
+
const lastMessage = messages.value.findLast((m) => m.role === "assistant" && m.streaming) || messages.value.findLast((m) => m.role === "assistant");
|
|
140
|
+
if (lastMessage) {
|
|
141
|
+
if (!lastMessage.contentBlocks) {
|
|
142
|
+
lastMessage.contentBlocks = [];
|
|
143
|
+
}
|
|
144
|
+
lastMessage.contentBlocks.push(resultBlock);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
socket.value.on("stream:text_delta", (data) => {
|
|
148
|
+
const lastMessage = messages.value.findLast((m) => m.role === "assistant" && m.streaming);
|
|
149
|
+
if (lastMessage) {
|
|
150
|
+
lastMessage.content += data.text;
|
|
151
|
+
if (!lastMessage.contentBlocks) {
|
|
152
|
+
lastMessage.contentBlocks = [];
|
|
153
|
+
}
|
|
154
|
+
const lastBlock = lastMessage.contentBlocks[lastMessage.contentBlocks.length - 1];
|
|
155
|
+
if (lastBlock && lastBlock.type === "text") {
|
|
156
|
+
lastBlock.text = (lastBlock.text || "") + data.text;
|
|
157
|
+
} else {
|
|
158
|
+
const prefix = lastMessage.contentBlocks.length > 0 ? "\n" : "";
|
|
159
|
+
lastMessage.contentBlocks.push({
|
|
160
|
+
type: "text",
|
|
161
|
+
text: prefix + data.text
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
socket.value.on("stream:message_complete", (data) => {
|
|
167
|
+
log("Message complete:", data.id);
|
|
168
|
+
const lastMessage = messages.value.findLast((m) => m.role === "assistant" && m.streaming) || messages.value.findLast((m) => m.role === "assistant");
|
|
169
|
+
if (lastMessage) {
|
|
170
|
+
lastMessage.streaming = false;
|
|
171
|
+
lastMessage.content = data.content;
|
|
172
|
+
lastMessage.contentBlocks = data.contentBlocks;
|
|
173
|
+
lastMessage.model = data.model;
|
|
174
|
+
}
|
|
175
|
+
pendingToolCalls.value.clear();
|
|
176
|
+
});
|
|
177
|
+
socket.value.on("output:error", (error) => {
|
|
178
|
+
log("Output error:", error);
|
|
179
|
+
addMessage("system", `Error: ${error}`);
|
|
180
|
+
});
|
|
181
|
+
socket.value.on("session:error", (error) => {
|
|
182
|
+
log("Session error:", error);
|
|
183
|
+
addMessage("system", `Session error: ${error}`);
|
|
184
|
+
});
|
|
185
|
+
socket.value.on("stream:system_message", (data) => {
|
|
186
|
+
log("System message:", data.message);
|
|
187
|
+
addMessage("system", data.message);
|
|
188
|
+
});
|
|
189
|
+
socket.value.on("stream:user_message", (data) => {
|
|
190
|
+
const currentUserId = getCurrentUserId?.();
|
|
191
|
+
log("User message received:", { senderId: data.senderId, currentUserId, isOwn: data.senderId === currentUserId });
|
|
192
|
+
if (currentUserId && data.senderId === currentUserId) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
messages.value.push({
|
|
196
|
+
...data,
|
|
197
|
+
timestamp: new Date(data.timestamp)
|
|
198
|
+
});
|
|
199
|
+
addMessage("assistant", "", true);
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
function newChat() {
|
|
203
|
+
if (socket.value) {
|
|
204
|
+
socket.value.emit("session:reset");
|
|
205
|
+
messages.value = [];
|
|
206
|
+
isHistoryOpen.value = false;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
function sendMessage(content, senderId, senderNickname) {
|
|
210
|
+
if (!content.trim() || isProcessing.value || !isConnected.value) return false;
|
|
211
|
+
const userMsg = addMessage("user", content);
|
|
212
|
+
if (senderId) {
|
|
213
|
+
userMsg.senderId = senderId;
|
|
214
|
+
userMsg.senderNickname = senderNickname;
|
|
215
|
+
}
|
|
216
|
+
addMessage("assistant", "", true);
|
|
217
|
+
if (socket.value) {
|
|
218
|
+
socket.value.emit("message:send", senderId ? { message: content, senderId } : content);
|
|
219
|
+
}
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
222
|
+
function toggleHistory() {
|
|
223
|
+
isHistoryOpen.value = !isHistoryOpen.value;
|
|
224
|
+
if (isHistoryOpen.value && socket.value) {
|
|
225
|
+
socket.value.emit("history:list");
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
function selectConversation(id) {
|
|
229
|
+
if (socket.value) {
|
|
230
|
+
socket.value.emit("history:switch", id);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
function disconnect() {
|
|
234
|
+
if (socket.value) {
|
|
235
|
+
socket.value.disconnect();
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
function findToolResult(blocks, toolUseId) {
|
|
239
|
+
if (!blocks) return void 0;
|
|
240
|
+
return blocks.find((b) => b.type === "tool_result" && b.tool_use_id === toolUseId);
|
|
241
|
+
}
|
|
242
|
+
return {
|
|
243
|
+
// State
|
|
244
|
+
socket,
|
|
245
|
+
messages,
|
|
246
|
+
conversations,
|
|
247
|
+
activeConversationId,
|
|
248
|
+
isConnected,
|
|
249
|
+
isSessionActive,
|
|
250
|
+
isProcessing,
|
|
251
|
+
isHistoryOpen,
|
|
252
|
+
statusText,
|
|
253
|
+
statusColor,
|
|
254
|
+
// Methods
|
|
255
|
+
connectSocket,
|
|
256
|
+
disconnect,
|
|
257
|
+
newChat,
|
|
258
|
+
sendMessage,
|
|
259
|
+
addMessage,
|
|
260
|
+
toggleHistory,
|
|
261
|
+
selectConversation,
|
|
262
|
+
findToolResult
|
|
263
|
+
};
|
|
264
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Socket } from 'socket.io-client';
|
|
2
|
+
import type { ShareUser } from '../types.js';
|
|
3
|
+
interface ShareOptions {
|
|
4
|
+
/** Get tunnel URL for share links */
|
|
5
|
+
getTunnelUrl?: () => string | null;
|
|
6
|
+
/** Log function */
|
|
7
|
+
log?: (...args: unknown[]) => void;
|
|
8
|
+
}
|
|
9
|
+
export declare function useShare(options?: ShareOptions): {
|
|
10
|
+
userId: import("vue").Ref<string | null, string | null>;
|
|
11
|
+
nickname: import("vue").Ref<string | null, string | null>;
|
|
12
|
+
users: import("vue").Ref<{
|
|
13
|
+
id: string;
|
|
14
|
+
nickname: string;
|
|
15
|
+
joinedAt: string;
|
|
16
|
+
lastSeen: string;
|
|
17
|
+
}[], ShareUser[] | {
|
|
18
|
+
id: string;
|
|
19
|
+
nickname: string;
|
|
20
|
+
joinedAt: string;
|
|
21
|
+
lastSeen: string;
|
|
22
|
+
}[]>;
|
|
23
|
+
showNicknameModal: import("vue").Ref<boolean, boolean>;
|
|
24
|
+
nicknameError: import("vue").Ref<string | null, string | null>;
|
|
25
|
+
isShareMode: import("vue").ComputedRef<boolean>;
|
|
26
|
+
wasInvited: import("vue").Ref<boolean, boolean>;
|
|
27
|
+
sharingActiveOnServer: import("vue").Ref<boolean, boolean>;
|
|
28
|
+
nicknameFromUrl: import("vue").Ref<boolean, boolean>;
|
|
29
|
+
initShare: () => void;
|
|
30
|
+
createShareLink: (includeNickname?: boolean) => string;
|
|
31
|
+
setNickname: (name: string) => void;
|
|
32
|
+
needsNicknameImmediate: () => boolean;
|
|
33
|
+
needsNicknameForMessage: () => boolean;
|
|
34
|
+
needsNicknameForShare: () => boolean;
|
|
35
|
+
checkSharingStatus: (socket: Socket | null) => void;
|
|
36
|
+
registerUser: (socket: Socket | null) => void;
|
|
37
|
+
setupSocketListeners: (socket: Socket) => void;
|
|
38
|
+
getNicknameById: (id: string) => string | null;
|
|
39
|
+
isOwnMessage: (senderId?: string) => boolean;
|
|
40
|
+
copyShareLink: (includeNickname?: boolean) => Promise<boolean>;
|
|
41
|
+
};
|
|
42
|
+
export {};
|