cli-jaw 1.6.11 → 1.6.13
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/dist/bin/commands/dispatch.js +41 -14
- package/dist/bin/commands/dispatch.js.map +1 -1
- package/dist/bin/commands/launchd.js +3 -2
- package/dist/bin/commands/launchd.js.map +1 -1
- package/dist/bin/commands/reset.js +1 -1
- package/dist/bin/commands/reset.js.map +1 -1
- package/dist/bin/commands/service.js +4 -3
- package/dist/bin/commands/service.js.map +1 -1
- package/dist/server.js +58 -7
- package/dist/server.js.map +1 -1
- package/dist/src/agent/lifecycle-handler.js +10 -4
- package/dist/src/agent/lifecycle-handler.js.map +1 -1
- package/dist/src/agent/spawn.js +48 -13
- package/dist/src/agent/spawn.js.map +1 -1
- package/dist/src/cli/handlers-runtime.js +4 -1
- package/dist/src/cli/handlers-runtime.js.map +1 -1
- package/dist/src/cli/handlers.js +4 -4
- package/dist/src/cli/handlers.js.map +1 -1
- package/dist/src/core/config.js +40 -3
- package/dist/src/core/config.js.map +1 -1
- package/dist/src/core/db.js +41 -0
- package/dist/src/core/db.js.map +1 -1
- package/dist/src/core/instance.js +18 -4
- package/dist/src/core/instance.js.map +1 -1
- package/dist/src/core/main-session.js +12 -3
- package/dist/src/core/main-session.js.map +1 -1
- package/dist/src/core/runtime-path.js +69 -0
- package/dist/src/core/runtime-path.js.map +1 -0
- package/dist/src/memory/indexing.js +1 -0
- package/dist/src/memory/indexing.js.map +1 -1
- package/dist/src/memory/shared.js +48 -15
- package/dist/src/memory/shared.js.map +1 -1
- package/dist/src/orchestrator/pipeline.js +3 -1
- package/dist/src/orchestrator/pipeline.js.map +1 -1
- package/dist/src/routes/avatar.js +120 -0
- package/dist/src/routes/avatar.js.map +1 -0
- package/dist/src/telegram/bot.js +7 -1
- package/dist/src/telegram/bot.js.map +1 -1
- package/package.json +1 -1
- package/public/css/chat.css +46 -2
- package/public/css/layout.css +26 -0
- package/public/css/variables.css +15 -0
- package/public/dist/assets/architecture-YZFGNWBL-ztGcIsMX.js +1 -0
- package/public/dist/assets/architectureDiagram-Q4EWVU46-BeLDNmwN.js +1 -0
- package/public/dist/assets/blockDiagram-DXYQGD6D-ChvfvChp.js +1 -0
- package/public/dist/assets/c4Diagram-AHTNJAMY-BzjAXXeE.js +1 -0
- package/public/dist/assets/classDiagram-6PBFFD2Q-Ce2Zjeeb.js +1 -0
- package/public/dist/assets/classDiagram-v2-HSJHXN6E-DGGliiix.js +1 -0
- package/public/dist/assets/cose-bilkent-S5V4N54A-T-OwcThC.js +1 -0
- package/public/dist/assets/dagre-KV5264BT-DmdK-8pb.js +1 -0
- package/public/dist/assets/diagram-5BDNPKRD-Bf8S6ACH.js +1 -0
- package/public/dist/assets/diagram-G4DWMVQ6-C37hs32X.js +1 -0
- package/public/dist/assets/diagram-MMDJMWI5-9NNeBmGR.js +1 -0
- package/public/dist/assets/diagram-TYMM5635-l-8r-KVr.js +1 -0
- package/public/dist/assets/{employees-km3MrgRX.js → employees-C2G0-Rg9.js} +1 -1
- package/public/dist/assets/erDiagram-SMLLAGMA-BfSqi5hi.js +1 -0
- package/public/dist/assets/flowDiagram-DWJPFMVM-B6am8zYz.js +1 -0
- package/public/dist/assets/ganttDiagram-T4ZO3ILL-weu0QfVv.js +1 -0
- package/public/dist/assets/gitGraph-7Q5UKJZL-BucTBJ0C.js +1 -0
- package/public/dist/assets/gitGraphDiagram-UUTBAWPF-D4BnXgVC.js +1 -0
- package/public/dist/assets/idb-cache-C7z4qE00.js +1 -0
- package/public/dist/assets/idb-cache-DbK81tgv.js +1 -0
- package/public/dist/assets/{index-DbSvUuCm.css → index-CDdXQQmm.css} +1 -1
- package/public/dist/assets/{index-CihhAsFo.js → index-CIWCSFl-.js} +9 -9
- package/public/dist/assets/info-OMHHGYJF-OpOBLEsS.js +1 -0
- package/public/dist/assets/infoDiagram-42DDH7IO-9W0kVtoy.js +1 -0
- package/public/dist/assets/ishikawaDiagram-UXIWVN3A-BEgeMMA5.js +1 -0
- package/public/dist/assets/journeyDiagram-VCZTEJTY-gIlNwmx5.js +1 -0
- package/public/dist/assets/kanban-definition-6JOO6SKY-DV9gfO6_.js +1 -0
- package/public/dist/assets/mermaid.core-CYqc8Qyq.js +1 -0
- package/public/dist/assets/mindmap-definition-QFDTVHPH-DFHJRlCi.js +1 -0
- package/public/dist/assets/packet-4T2RLAQJ-DxyOEAi5.js +1 -0
- package/public/dist/assets/pie-ZZUOXDRM-CU7m5wDm.js +1 -0
- package/public/dist/assets/pieDiagram-DEJITSTG-BEGiEzHN.js +1 -0
- package/public/dist/assets/quadrantDiagram-34T5L4WZ-E5jEMjzC.js +1 -0
- package/public/dist/assets/radar-PYXPWWZC-CNpXegnm.js +1 -0
- package/public/dist/assets/{render-CCNMcx8O.js → render-BFAkzW1S.js} +1 -1
- package/public/dist/assets/requirementDiagram-MS252O5E-B3fjwTBx.js +1 -0
- package/public/dist/assets/sankeyDiagram-XADWPNL6-BIyouHFw.js +1 -0
- package/public/dist/assets/sequenceDiagram-FGHM5R23-CROknRPY.js +1 -0
- package/public/dist/assets/{settings-de6iL7ha.js → settings-BtX9STQd.js} +1 -1
- package/public/dist/assets/settings-DUWhygHi.js +1 -0
- package/public/dist/assets/skills-C6aTdbWY.js +1 -0
- package/public/dist/assets/{skills-CiZtWh5G.js → skills-C9o5E1Pc.js} +1 -1
- package/public/dist/assets/slash-commands-C1p8kRBv.js +1 -0
- package/public/dist/assets/{slash-commands-BeZm3K9x.js → slash-commands-DveLHSQt.js} +1 -1
- package/public/dist/assets/stateDiagram-FHFEXIEX-DqgrX77_.js +1 -0
- package/public/dist/assets/stateDiagram-v2-QKLJ7IA2-CMxISNLC.js +1 -0
- package/public/dist/assets/timeline-definition-GMOUNBTQ-CheLYWOf.js +1 -0
- package/public/dist/assets/treeView-SZITEDCU-CWgmTXw-.js +1 -0
- package/public/dist/assets/treemap-W4RFUUIX-CMkxaF1N.js +1 -0
- package/public/dist/assets/ui-BpZlLDtM.js +1 -0
- package/public/dist/assets/ui-Dx3w-H-4.js +131 -0
- package/public/dist/assets/{vendor-mermaid-DV2i2BfY.js → vendor-mermaid-C2RBgdM6.js} +4 -4
- package/public/dist/assets/vennDiagram-DHZGUBPP-Cm4YZzbv.js +1 -0
- package/public/dist/assets/wardley-RL74JXVD-HPEa5s3y.js +1 -0
- package/public/dist/assets/wardleyDiagram-NUSXRM2D-vZvODvIY.js +1 -0
- package/public/dist/assets/{ws-DQ4lNPWK.js → ws-D39_cIa_.js} +1 -1
- package/public/dist/assets/xychartDiagram-5P7HB3ND-DdPllF4_.js +1 -0
- package/public/dist/index.html +25 -2
- package/public/index.html +23 -0
- package/public/js/features/avatar.ts +224 -0
- package/public/js/features/chat.ts +1 -1
- package/public/js/features/idb-cache.ts +5 -0
- package/public/js/main.ts +2 -0
- package/public/js/ui.ts +23 -11
- package/public/js/uuid.ts +24 -0
- package/public/js/virtual-scroll.ts +21 -5
- package/public/js/ws.ts +9 -3
- package/public/locales/en.json +2 -3
- package/public/locales/ko.json +2 -3
- package/public/dist/assets/architecture-YZFGNWBL-BBAoUgDs.js +0 -1
- package/public/dist/assets/architectureDiagram-Q4EWVU46-CnCGC-W1.js +0 -1
- package/public/dist/assets/blockDiagram-DXYQGD6D-CCKUN-eq.js +0 -1
- package/public/dist/assets/c4Diagram-AHTNJAMY-2BznTVIo.js +0 -1
- package/public/dist/assets/classDiagram-6PBFFD2Q-CNIdDcl_.js +0 -1
- package/public/dist/assets/classDiagram-v2-HSJHXN6E-D5c5cCXM.js +0 -1
- package/public/dist/assets/cose-bilkent-S5V4N54A-15-I0CI3.js +0 -1
- package/public/dist/assets/dagre-KV5264BT-B-7UZ2bP.js +0 -1
- package/public/dist/assets/diagram-5BDNPKRD-CtpSLvPV.js +0 -1
- package/public/dist/assets/diagram-G4DWMVQ6-C-FxLjz8.js +0 -1
- package/public/dist/assets/diagram-MMDJMWI5-CyWYCa1y.js +0 -1
- package/public/dist/assets/diagram-TYMM5635-Bc2Z_pKL.js +0 -1
- package/public/dist/assets/erDiagram-SMLLAGMA-BOsddzBo.js +0 -1
- package/public/dist/assets/flowDiagram-DWJPFMVM-CNS6NI2y.js +0 -1
- package/public/dist/assets/ganttDiagram-T4ZO3ILL-DzeONbMw.js +0 -1
- package/public/dist/assets/gitGraph-7Q5UKJZL-D-msAZeS.js +0 -1
- package/public/dist/assets/gitGraphDiagram-UUTBAWPF-Ck2kMeHe.js +0 -1
- package/public/dist/assets/info-OMHHGYJF-38sLGsyT.js +0 -1
- package/public/dist/assets/infoDiagram-42DDH7IO-BpZBAgqS.js +0 -1
- package/public/dist/assets/ishikawaDiagram-UXIWVN3A-CHk3pDry.js +0 -1
- package/public/dist/assets/journeyDiagram-VCZTEJTY-BQ1Iwmix.js +0 -1
- package/public/dist/assets/kanban-definition-6JOO6SKY-B0_TgypH.js +0 -1
- package/public/dist/assets/mermaid.core-C6Yxaqu5.js +0 -1
- package/public/dist/assets/mindmap-definition-QFDTVHPH-Di38Ha2d.js +0 -1
- package/public/dist/assets/packet-4T2RLAQJ-WhSOczez.js +0 -1
- package/public/dist/assets/pie-ZZUOXDRM-BFlknSFa.js +0 -1
- package/public/dist/assets/pieDiagram-DEJITSTG-C580ezo8.js +0 -1
- package/public/dist/assets/quadrantDiagram-34T5L4WZ-CqL6mFQa.js +0 -1
- package/public/dist/assets/radar-PYXPWWZC-C6TfDjQ7.js +0 -1
- package/public/dist/assets/requirementDiagram-MS252O5E-CJ2UTxBk.js +0 -1
- package/public/dist/assets/sankeyDiagram-XADWPNL6-CtV7e3kn.js +0 -1
- package/public/dist/assets/sequenceDiagram-FGHM5R23-CK_ww48d.js +0 -1
- package/public/dist/assets/settings-Jzr8xYot.js +0 -1
- package/public/dist/assets/skills-DGAnOiRN.js +0 -1
- package/public/dist/assets/slash-commands-D5ZGh5wk.js +0 -1
- package/public/dist/assets/stateDiagram-FHFEXIEX-CY21kttq.js +0 -1
- package/public/dist/assets/stateDiagram-v2-QKLJ7IA2-Da4cAFSL.js +0 -1
- package/public/dist/assets/timeline-definition-GMOUNBTQ-BRNd_2SN.js +0 -1
- package/public/dist/assets/treeView-SZITEDCU-C4o4RBOK.js +0 -1
- package/public/dist/assets/treemap-W4RFUUIX-BIAsQVaf.js +0 -1
- package/public/dist/assets/ui-C1QJBOEL.js +0 -131
- package/public/dist/assets/ui-CxS6ErNQ.js +0 -1
- package/public/dist/assets/vennDiagram-DHZGUBPP-BE5I_z1u.js +0 -1
- package/public/dist/assets/wardley-RL74JXVD-BYUgx5S_.js +0 -1
- package/public/dist/assets/wardleyDiagram-NUSXRM2D-DZn9qJg1.js +0 -1
- package/public/dist/assets/xychartDiagram-5P7HB3ND-DIYzBAwh.js +0 -1
- /package/public/dist/assets/{constants-74vpnlVG.js → constants-DwGsi0Gn.js} +0 -0
- /package/public/dist/assets/{locale-DIXc-_34.js → locale-DVVWjxKN.js} +0 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { escapeHtml } from '../render.js';
|
|
2
|
+
import { api, getAuthToken } from '../api.js';
|
|
3
|
+
|
|
4
|
+
type AvatarRole = 'agent' | 'user';
|
|
5
|
+
type AvatarServerEntry = {
|
|
6
|
+
kind?: 'emoji' | 'image';
|
|
7
|
+
imageUrl?: string;
|
|
8
|
+
updatedAt?: number | null;
|
|
9
|
+
};
|
|
10
|
+
type AvatarServerState = Record<AvatarRole, AvatarServerEntry>;
|
|
11
|
+
type AvatarState = {
|
|
12
|
+
emoji: string;
|
|
13
|
+
imageUrl: string;
|
|
14
|
+
updatedAt: number | null;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const AGENT_KEY = 'agentAvatar';
|
|
18
|
+
const USER_KEY = 'userAvatar';
|
|
19
|
+
const DEFAULT_AGENT = '🦈';
|
|
20
|
+
const DEFAULT_USER = '👤';
|
|
21
|
+
|
|
22
|
+
const avatarState: Record<AvatarRole, AvatarState> = {
|
|
23
|
+
agent: { emoji: DEFAULT_AGENT, imageUrl: '', updatedAt: null },
|
|
24
|
+
user: { emoji: DEFAULT_USER, imageUrl: '', updatedAt: null },
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
let initialized = false;
|
|
28
|
+
|
|
29
|
+
function stateFor(role: AvatarRole): AvatarState {
|
|
30
|
+
return avatarState[role];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function defaultEmoji(role: AvatarRole): string {
|
|
34
|
+
return role === 'agent' ? DEFAULT_AGENT : DEFAULT_USER;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function storageKey(role: AvatarRole): string {
|
|
38
|
+
return role === 'agent' ? AGENT_KEY : USER_KEY;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function inputId(role: AvatarRole): string {
|
|
42
|
+
return role === 'agent' ? 'agentAvatarInput' : 'userAvatarInput';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function statusId(role: AvatarRole): string {
|
|
46
|
+
return role === 'agent' ? 'agentAvatarStatus' : 'userAvatarStatus';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function iconSelector(role: AvatarRole): string {
|
|
50
|
+
return role === 'agent' ? '.agent-icon' : '.user-icon';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function getEmoji(role: AvatarRole): string {
|
|
54
|
+
return stateFor(role).emoji;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function syncInputs(role: AvatarRole): void {
|
|
58
|
+
const input = document.getElementById(inputId(role)) as HTMLInputElement | null;
|
|
59
|
+
if (input) input.value = getEmoji(role);
|
|
60
|
+
const status = document.getElementById(statusId(role));
|
|
61
|
+
if (status) status.textContent = stateFor(role).imageUrl ? 'image active' : 'emoji active';
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function setStatus(role: AvatarRole, text: string): void {
|
|
65
|
+
const status = document.getElementById(statusId(role));
|
|
66
|
+
if (status) status.textContent = text;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function avatarMarkup(role: AvatarRole): string {
|
|
70
|
+
const current = stateFor(role);
|
|
71
|
+
if (current.imageUrl) {
|
|
72
|
+
return `<img class="avatar-image" src="${escapeHtml(current.imageUrl)}" alt="" loading="lazy" decoding="async">`;
|
|
73
|
+
}
|
|
74
|
+
return escapeHtml(current.emoji);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function applyAvatar(role: AvatarRole): void {
|
|
78
|
+
const html = avatarMarkup(role);
|
|
79
|
+
const kind = stateFor(role).imageUrl ? 'image' : 'emoji';
|
|
80
|
+
document.querySelectorAll(iconSelector(role)).forEach((el) => {
|
|
81
|
+
el.innerHTML = html;
|
|
82
|
+
el.setAttribute('data-avatar-kind', kind);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function setServerAvatar(role: AvatarRole, payload?: AvatarServerEntry | null): void {
|
|
87
|
+
if (payload?.kind === 'image' && payload.imageUrl) {
|
|
88
|
+
stateFor(role).imageUrl = payload.imageUrl;
|
|
89
|
+
stateFor(role).updatedAt = payload.updatedAt ?? Date.now();
|
|
90
|
+
} else {
|
|
91
|
+
stateFor(role).imageUrl = '';
|
|
92
|
+
stateFor(role).updatedAt = payload?.updatedAt ?? null;
|
|
93
|
+
}
|
|
94
|
+
syncInputs(role);
|
|
95
|
+
applyAvatar(role);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async function loadServerAvatars(): Promise<void> {
|
|
99
|
+
const payload = await api<AvatarServerState>('/api/avatar');
|
|
100
|
+
if (!payload) return;
|
|
101
|
+
setServerAvatar('agent', payload.agent);
|
|
102
|
+
setServerAvatar('user', payload.user);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function authorizedFetch(path: string, init: RequestInit): Promise<Response> {
|
|
106
|
+
const token = await getAuthToken();
|
|
107
|
+
const headers = new Headers(init.headers || {});
|
|
108
|
+
if (token) headers.set('Authorization', `Bearer ${token}`);
|
|
109
|
+
return fetch(path, { ...init, headers });
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function uploadAvatar(role: AvatarRole, file: File): Promise<void> {
|
|
113
|
+
setStatus(role, 'uploading...');
|
|
114
|
+
const res = await authorizedFetch(`/api/avatar/${role}/upload`, {
|
|
115
|
+
method: 'POST',
|
|
116
|
+
headers: { 'X-Filename': encodeURIComponent(file.name) },
|
|
117
|
+
body: file,
|
|
118
|
+
});
|
|
119
|
+
const json = await res.json().catch(() => null);
|
|
120
|
+
if (!res.ok) {
|
|
121
|
+
setStatus(role, 'upload failed');
|
|
122
|
+
throw new Error(json?.error || `avatar upload failed (${res.status})`);
|
|
123
|
+
}
|
|
124
|
+
setServerAvatar(role, json?.data || json);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async function resetAvatarImage(role: AvatarRole): Promise<void> {
|
|
128
|
+
setStatus(role, 'resetting...');
|
|
129
|
+
const res = await authorizedFetch(`/api/avatar/${role}/image`, { method: 'DELETE' });
|
|
130
|
+
const json = await res.json().catch(() => null);
|
|
131
|
+
if (!res.ok) {
|
|
132
|
+
setStatus(role, 'reset failed');
|
|
133
|
+
throw new Error(json?.error || `avatar reset failed (${res.status})`);
|
|
134
|
+
}
|
|
135
|
+
setServerAvatar(role, json?.data || json);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function bindRoleControls(role: AvatarRole): void {
|
|
139
|
+
const uploadBtnId = role === 'agent' ? 'agentAvatarUploadBtn' : 'userAvatarUploadBtn';
|
|
140
|
+
const resetBtnId = role === 'agent' ? 'agentAvatarResetBtn' : 'userAvatarResetBtn';
|
|
141
|
+
const fileInputId = role === 'agent' ? 'agentAvatarFile' : 'userAvatarFile';
|
|
142
|
+
|
|
143
|
+
document.getElementById(uploadBtnId)?.addEventListener('click', () => {
|
|
144
|
+
(document.getElementById(fileInputId) as HTMLInputElement | null)?.click();
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
document.getElementById(resetBtnId)?.addEventListener('click', async () => {
|
|
148
|
+
try {
|
|
149
|
+
await resetAvatarImage(role);
|
|
150
|
+
} catch (error) {
|
|
151
|
+
console.warn('[avatar:reset]', (error as Error).message);
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
document.getElementById(fileInputId)?.addEventListener('change', async (event: Event) => {
|
|
156
|
+
const input = event.target as HTMLInputElement;
|
|
157
|
+
const file = input.files?.[0];
|
|
158
|
+
if (!file) return;
|
|
159
|
+
try {
|
|
160
|
+
await uploadAvatar(role, file);
|
|
161
|
+
} catch (error) {
|
|
162
|
+
console.warn('[avatar:upload]', (error as Error).message);
|
|
163
|
+
} finally {
|
|
164
|
+
input.value = '';
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export function getAgentAvatar(): string { return getEmoji('agent'); }
|
|
170
|
+
export function getUserAvatar(): string { return getEmoji('user'); }
|
|
171
|
+
export function getAgentAvatarMarkup(): string { return avatarMarkup('agent'); }
|
|
172
|
+
export function getUserAvatarMarkup(): string { return avatarMarkup('user'); }
|
|
173
|
+
|
|
174
|
+
export function setAgentAvatar(emoji: string): void {
|
|
175
|
+
const next = (emoji || '').trim() || DEFAULT_AGENT;
|
|
176
|
+
stateFor('agent').emoji = next;
|
|
177
|
+
localStorage.setItem(storageKey('agent'), next);
|
|
178
|
+
syncInputs('agent');
|
|
179
|
+
if (!stateFor('agent').imageUrl) applyAvatar('agent');
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export function setUserAvatar(emoji: string): void {
|
|
183
|
+
const next = (emoji || '').trim() || DEFAULT_USER;
|
|
184
|
+
stateFor('user').emoji = next;
|
|
185
|
+
localStorage.setItem(storageKey('user'), next);
|
|
186
|
+
syncInputs('user');
|
|
187
|
+
if (!stateFor('user').imageUrl) applyAvatar('user');
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export async function initAvatar(): Promise<void> {
|
|
191
|
+
stateFor('agent').emoji = localStorage.getItem(AGENT_KEY) || DEFAULT_AGENT;
|
|
192
|
+
stateFor('user').emoji = localStorage.getItem(USER_KEY) || DEFAULT_USER;
|
|
193
|
+
syncInputs('agent');
|
|
194
|
+
syncInputs('user');
|
|
195
|
+
|
|
196
|
+
if (!initialized) {
|
|
197
|
+
initialized = true;
|
|
198
|
+
|
|
199
|
+
document.getElementById('avatarSave')?.addEventListener('click', () => {
|
|
200
|
+
const agentInput = document.getElementById('agentAvatarInput') as HTMLInputElement | null;
|
|
201
|
+
const userInput = document.getElementById('userAvatarInput') as HTMLInputElement | null;
|
|
202
|
+
if (agentInput) setAgentAvatar(agentInput.value);
|
|
203
|
+
if (userInput) setUserAvatar(userInput.value);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
for (const id of ['agentAvatarInput', 'userAvatarInput']) {
|
|
207
|
+
document.getElementById(id)?.addEventListener('keydown', (e: Event) => {
|
|
208
|
+
const keyEvent = e as KeyboardEvent;
|
|
209
|
+
if (keyEvent.key === 'Enter') {
|
|
210
|
+
keyEvent.preventDefault();
|
|
211
|
+
document.getElementById('avatarSave')?.click();
|
|
212
|
+
(keyEvent.target as HTMLInputElement).blur();
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
bindRoleControls('agent');
|
|
218
|
+
bindRoleControls('user');
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
await loadServerAvatars();
|
|
222
|
+
applyAvatar('agent');
|
|
223
|
+
applyAvatar('user');
|
|
224
|
+
}
|
|
@@ -202,7 +202,7 @@ function renderFilePreview(): void {
|
|
|
202
202
|
}
|
|
203
203
|
|
|
204
204
|
export async function clearChat(): Promise<void> {
|
|
205
|
-
|
|
205
|
+
// UI-only clear — do NOT call /api/clear (it deletes DB messages)
|
|
206
206
|
cancelPostRender();
|
|
207
207
|
getVirtualScroll().clear();
|
|
208
208
|
const chatEl = document.getElementById('chatMessages');
|
|
@@ -74,6 +74,11 @@ function openDB(): Promise<IDBDatabase> {
|
|
|
74
74
|
|
|
75
75
|
export async function cacheMessages(messages: CachedMessage[]): Promise<void> {
|
|
76
76
|
try {
|
|
77
|
+
// Guard: never wipe the cache with an empty array — only a deliberate
|
|
78
|
+
// clearCache() call should empty the store. This prevents data loss when
|
|
79
|
+
// the server briefly returns [] during restarts or scope mismatches.
|
|
80
|
+
if (messages.length === 0) return;
|
|
81
|
+
|
|
77
82
|
const db = await openDB();
|
|
78
83
|
const tx = db.transaction(STORE, 'readwrite');
|
|
79
84
|
const store = tx.objectStore(STORE);
|
package/public/js/main.ts
CHANGED
|
@@ -49,6 +49,7 @@ import {
|
|
|
49
49
|
import { state } from './state.js';
|
|
50
50
|
import { loadCliRegistry, getCliKeys } from './constants.js';
|
|
51
51
|
import { initAppName } from './features/appname.js';
|
|
52
|
+
import { initAvatar } from './features/avatar.js';
|
|
52
53
|
import { initSidebar, toggleLeft, toggleRight } from './features/sidebar.js';
|
|
53
54
|
import { initTheme } from './features/theme.js';
|
|
54
55
|
import { initGestures } from './features/gesture.js';
|
|
@@ -431,6 +432,7 @@ async function bootstrap(): Promise<void> {
|
|
|
431
432
|
loadEmployees();
|
|
432
433
|
initHeartbeatBadge();
|
|
433
434
|
initAppName();
|
|
435
|
+
await initAvatar();
|
|
434
436
|
initSidebar();
|
|
435
437
|
initMsgCopy();
|
|
436
438
|
initGestures();
|
package/public/js/ui.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
// ── UI Utilities ──
|
|
2
2
|
import { state } from './state.js';
|
|
3
3
|
import { renderMarkdown, escapeHtml, stripOrchestration, linkifyFilePaths } from './render.js';
|
|
4
|
+
import { generateId } from './uuid.js';
|
|
4
5
|
import { getAppName } from './features/appname.js';
|
|
6
|
+
import { getAgentAvatarMarkup, getUserAvatarMarkup } from './features/avatar.js';
|
|
5
7
|
import { t } from './features/i18n.js';
|
|
6
8
|
import { api } from './api.js';
|
|
7
9
|
import { cacheMessages, getCachedMessages, appendCachedMessage, upsertMessage, setMessageScope, getScopedMessages } from './features/idb-cache.js';
|
|
@@ -34,13 +36,12 @@ function parseToolLog(toolLog?: string | null): ToolLogEntry[] {
|
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
function getAgentIcon(_cli?: string | null): string {
|
|
37
|
-
|
|
38
|
-
return ICONS.shark;
|
|
39
|
+
return getAgentAvatarMarkup();
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
function toProcessSteps(tools: ToolLogEntry[]): ProcessStep[] {
|
|
42
43
|
return tools.map((tool: any) => ({
|
|
43
|
-
id:
|
|
44
|
+
id: generateId(),
|
|
44
45
|
icon: tool.icon ? emojiToIcon(tool.icon) : ICONS.tool,
|
|
45
46
|
label: tool.label || tool.name || 'tool',
|
|
46
47
|
type: tool.toolType || 'tool',
|
|
@@ -295,7 +296,7 @@ export function addMessage(role: string, text: string, cli?: string | null): HTM
|
|
|
295
296
|
div.innerHTML = `<div class="agent-icon" aria-hidden="true">${getAgentIcon(cli)}</div><div class="agent-body"><div class="msg-content">${rendered}</div><button class="msg-copy" title="Copy" aria-label="Copy message"></button></div>`;
|
|
296
297
|
} else {
|
|
297
298
|
div.className = `msg msg-${role}`;
|
|
298
|
-
div.innerHTML = `<div class="msg-label">${label}</div><div class="msg-content">${rendered}</div><button class="msg-copy" title="Copy" aria-label="Copy message"></button>`;
|
|
299
|
+
div.innerHTML = `<div class="user-body"><div class="msg-label">${label}</div><div class="msg-content">${rendered}</div><button class="msg-copy" title="Copy" aria-label="Copy message"></button></div><div class="user-icon" aria-hidden="true">${getUserAvatarMarkup()}</div>`;
|
|
299
300
|
}
|
|
300
301
|
const contentEl = div.querySelector('.msg-content');
|
|
301
302
|
if (contentEl) contentEl.setAttribute('data-raw', stripOrchestration(text));
|
|
@@ -316,7 +317,7 @@ export function addMessage(role: string, text: string, cli?: string | null): HTM
|
|
|
316
317
|
if (msgCount >= VS_THRESHOLD) {
|
|
317
318
|
// Feed all existing DOM messages into VS items array
|
|
318
319
|
container.querySelectorAll('.msg').forEach(el => {
|
|
319
|
-
vs.addItem(
|
|
320
|
+
vs.addItem(generateId(), el.outerHTML);
|
|
320
321
|
});
|
|
321
322
|
// Wire widget activation + file path linkification for VS-rendered items
|
|
322
323
|
vs.onPostRender = (viewport: HTMLElement) => {
|
|
@@ -333,6 +334,11 @@ export function addMessage(role: string, text: string, cli?: string | null): HTM
|
|
|
333
334
|
let scrollRAF: number | null = null;
|
|
334
335
|
|
|
335
336
|
export function scrollToBottom(): void {
|
|
337
|
+
const vs = getVirtualScroll();
|
|
338
|
+
if (vs.active) {
|
|
339
|
+
vs.scrollToBottom();
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
336
342
|
if (scrollRAF) return;
|
|
337
343
|
scrollRAF = requestAnimationFrame(() => {
|
|
338
344
|
scrollRAF = null;
|
|
@@ -368,11 +374,15 @@ export function handleSave(): void {
|
|
|
368
374
|
}
|
|
369
375
|
}
|
|
370
376
|
|
|
377
|
+
function updateStatMsgs(count: number): void {
|
|
378
|
+
const el = document.getElementById('statMsgs');
|
|
379
|
+
if (el) el.textContent = t('stat.messages', { count });
|
|
380
|
+
}
|
|
381
|
+
|
|
371
382
|
export async function loadStats(): Promise<void> {
|
|
372
383
|
const msgs = await api<MessageItem[]>('/api/messages');
|
|
373
384
|
if (!msgs) return;
|
|
374
|
-
|
|
375
|
-
if (el) el.textContent = t('stat.messages', { count: msgs.length });
|
|
385
|
+
updateStatMsgs(msgs.length);
|
|
376
386
|
}
|
|
377
387
|
|
|
378
388
|
export async function loadMessages(): Promise<void> {
|
|
@@ -403,8 +413,8 @@ export async function loadMessages(): Promise<void> {
|
|
|
403
413
|
const skeletonContent = '<div class="skeleton-line"></div><div class="skeleton-line"></div>';
|
|
404
414
|
const html = role === 'agent'
|
|
405
415
|
? `<div class="msg msg-agent"><div class="agent-icon" aria-hidden="true">${getAgentIcon(m.cli)}</div><div class="agent-body">${toolHtml}<div class="msg-content lazy-pending" data-raw="${escapeHtml(rawContent)}">${skeletonContent}</div><button class="msg-copy" title="Copy" aria-label="Copy message"></button></div></div>`
|
|
406
|
-
: `<div class="msg msg-${role}"><div class="msg-label">${label}</div><div class="msg-content lazy-pending" data-raw="${escapeHtml(rawContent)}">${skeletonContent}</div><button class="msg-copy" title="Copy" aria-label="Copy message"></button></div>`;
|
|
407
|
-
vs.addItem(
|
|
416
|
+
: `<div class="msg msg-${role}"><div class="user-body"><div class="msg-label">${label}</div><div class="msg-content lazy-pending" data-raw="${escapeHtml(rawContent)}">${skeletonContent}</div><button class="msg-copy" title="Copy" aria-label="Copy message"></button></div><div class="user-icon" aria-hidden="true">${getUserAvatarMarkup()}</div></div>`;
|
|
417
|
+
vs.addItem(generateId(), html);
|
|
408
418
|
}
|
|
409
419
|
|
|
410
420
|
// Register lazy render callback
|
|
@@ -452,6 +462,7 @@ export async function loadMessages(): Promise<void> {
|
|
|
452
462
|
cacheMessages(msgs.map(m => ({
|
|
453
463
|
role: m.role, content: m.content, cli: m.cli ?? null, tool_log: m.tool_log ?? null, timestamp: Date.now(),
|
|
454
464
|
}))).catch(() => {});
|
|
465
|
+
updateStatMsgs(msgs.length);
|
|
455
466
|
showEmptyState();
|
|
456
467
|
return;
|
|
457
468
|
}
|
|
@@ -474,8 +485,8 @@ export async function loadMessages(): Promise<void> {
|
|
|
474
485
|
const skeletonContent = '<div class="skeleton-line"></div><div class="skeleton-line"></div>';
|
|
475
486
|
const html = role === 'agent'
|
|
476
487
|
? `<div class="msg msg-agent"><div class="agent-icon" aria-hidden="true">${getAgentIcon(m.cli)}</div><div class="agent-body">${toolHtml}<div class="msg-content lazy-pending" data-raw="${escapeHtml(rawContent)}">${skeletonContent}</div><button class="msg-copy" title="Copy" aria-label="Copy message"></button></div></div>`
|
|
477
|
-
: `<div class="msg msg-${role}"><div class="msg-label">${label}</div><div class="msg-content lazy-pending" data-raw="${escapeHtml(rawContent)}">${skeletonContent}</div><button class="msg-copy" title="Copy" aria-label="Copy message"></button></div>`;
|
|
478
|
-
vs.addItem(
|
|
488
|
+
: `<div class="msg msg-${role}"><div class="user-body"><div class="msg-label">${label}</div><div class="msg-content lazy-pending" data-raw="${escapeHtml(rawContent)}">${skeletonContent}</div><button class="msg-copy" title="Copy" aria-label="Copy message"></button></div><div class="user-icon" aria-hidden="true">${getUserAvatarMarkup()}</div></div>`;
|
|
489
|
+
vs.addItem(generateId(), html);
|
|
479
490
|
}
|
|
480
491
|
vs.onLazyRender = (targets: HTMLElement[]) => {
|
|
481
492
|
for (const el of targets) {
|
|
@@ -513,6 +524,7 @@ export async function loadMessages(): Promise<void> {
|
|
|
513
524
|
});
|
|
514
525
|
}
|
|
515
526
|
addSystemMsg(`${ICONS.warning} 오프라인 모드 — 캐시된 메시지 표시 중`);
|
|
527
|
+
updateStatMsgs(cached.length);
|
|
516
528
|
}
|
|
517
529
|
showEmptyState();
|
|
518
530
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// ── UUID Utility ──
|
|
2
|
+
/**
|
|
3
|
+
* Secure-context-safe UUID v4 generator.
|
|
4
|
+
* crypto.randomUUID() requires Secure Context (HTTPS / localhost).
|
|
5
|
+
* Fallback chain: randomUUID → getRandomValues (RFC 4122) → Math.random.
|
|
6
|
+
* Math.random tier is NOT cryptographically secure — used only for UI element IDs.
|
|
7
|
+
*/
|
|
8
|
+
export function generateId(): string {
|
|
9
|
+
const c = globalThis.crypto;
|
|
10
|
+
if (typeof c?.randomUUID === 'function') return c.randomUUID();
|
|
11
|
+
if (typeof c?.getRandomValues !== 'function') {
|
|
12
|
+
// Last resort: Math.random (never reached in modern browsers)
|
|
13
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, ch => {
|
|
14
|
+
const r = (Math.random() * 16) | 0;
|
|
15
|
+
return (ch === 'x' ? r : (r & 0x3) | 0x8).toString(16);
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
const bytes = new Uint8Array(16);
|
|
19
|
+
c.getRandomValues(bytes);
|
|
20
|
+
bytes[6] = (bytes[6] & 0x0f) | 0x40;
|
|
21
|
+
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
|
22
|
+
const hex = Array.from(bytes, b => b.toString(16).padStart(2, '0')).join('');
|
|
23
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
24
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// ── Virtual Scroll ──
|
|
2
|
+
import { generateId } from './uuid.js';
|
|
2
3
|
// Activates at THRESHOLD messages to prevent DOM bloat
|
|
3
4
|
// Below threshold: standard DOM append (zero overhead)
|
|
4
5
|
|
|
@@ -47,6 +48,7 @@ export class VirtualScroll {
|
|
|
47
48
|
* Called on conversation clear or explicit reset. */
|
|
48
49
|
flushToDOM(): void {
|
|
49
50
|
if (!this._active) return;
|
|
51
|
+
this.container.classList.remove('vs-active');
|
|
50
52
|
this.container.removeEventListener('scroll', this.scrollHandler);
|
|
51
53
|
if (this.rafId) { cancelAnimationFrame(this.rafId); this.rafId = null; }
|
|
52
54
|
this.container.innerHTML = this.items.map(it => it.html).join('');
|
|
@@ -74,13 +76,13 @@ export class VirtualScroll {
|
|
|
74
76
|
appendLiveItem(div: HTMLElement): void {
|
|
75
77
|
if (!this._active) return;
|
|
76
78
|
const html = div.outerHTML;
|
|
77
|
-
const id =
|
|
79
|
+
const id = generateId();
|
|
78
80
|
const item: VirtualItem = { id, html, height: EST_HEIGHT };
|
|
79
81
|
this.items.push(item);
|
|
80
82
|
this._totalHeight += EST_HEIGHT;
|
|
81
83
|
// Render immediately then scroll again after height is remeasured
|
|
82
84
|
this.render();
|
|
83
|
-
this.
|
|
85
|
+
this.scrollToBottom();
|
|
84
86
|
}
|
|
85
87
|
|
|
86
88
|
/** Update cached HTML for a specific item index (used by lazy render). */
|
|
@@ -99,10 +101,14 @@ export class VirtualScroll {
|
|
|
99
101
|
existing.forEach((el, i) => {
|
|
100
102
|
if (this.items[i]) {
|
|
101
103
|
this.items[i].height = el.getBoundingClientRect().height;
|
|
102
|
-
this._totalHeight += this.items[i].height;
|
|
103
104
|
}
|
|
104
105
|
});
|
|
106
|
+
// Rebuild _totalHeight from items array (covers both DOM-measured and estimated heights)
|
|
107
|
+
for (const item of this.items) {
|
|
108
|
+
this._totalHeight += item.height;
|
|
109
|
+
}
|
|
105
110
|
// Atomic swap — avoids visible blank frame during activation
|
|
111
|
+
this.container.classList.add('vs-active');
|
|
106
112
|
this.container.replaceChildren(this.spacerTop, this.viewport, this.spacerBottom);
|
|
107
113
|
this.container.addEventListener('scroll', this.scrollHandler, { passive: true });
|
|
108
114
|
this.render();
|
|
@@ -121,7 +127,7 @@ export class VirtualScroll {
|
|
|
121
127
|
const viewHeight = this.container.clientHeight;
|
|
122
128
|
|
|
123
129
|
let accum = 0;
|
|
124
|
-
let startIdx =
|
|
130
|
+
let startIdx = this.items.length - 1; // fallback to bottom, not top
|
|
125
131
|
for (let i = 0; i < this.items.length; i++) {
|
|
126
132
|
if (accum + this.items[i].height > scrollTop) {
|
|
127
133
|
startIdx = i;
|
|
@@ -215,6 +221,9 @@ export class VirtualScroll {
|
|
|
215
221
|
/** Batch-read heights from visible elements, batch-write to items array.
|
|
216
222
|
* Separated read/write passes = single forced reflow. */
|
|
217
223
|
private remeasureVisible(): void {
|
|
224
|
+
// Capture bottom-proximity BEFORE heights change
|
|
225
|
+
const wasAtBottom = this.container.scrollHeight - this.container.scrollTop - this.container.clientHeight < 80;
|
|
226
|
+
|
|
218
227
|
const rects: { idx: number; newH: number }[] = [];
|
|
219
228
|
this.viewport.querySelectorAll('[data-vs-idx]').forEach(el => {
|
|
220
229
|
const idx = Number((el as HTMLElement).dataset.vsIdx);
|
|
@@ -222,17 +231,23 @@ export class VirtualScroll {
|
|
|
222
231
|
rects.push({ idx, newH: el.getBoundingClientRect().height });
|
|
223
232
|
}
|
|
224
233
|
});
|
|
234
|
+
let heightChanged = false;
|
|
225
235
|
for (const { idx, newH } of rects) {
|
|
226
236
|
const oldH = this.items[idx].height;
|
|
227
237
|
if (oldH !== newH) {
|
|
228
238
|
this.items[idx].height = newH;
|
|
229
239
|
this._totalHeight += (newH - oldH);
|
|
240
|
+
heightChanged = true;
|
|
230
241
|
}
|
|
231
242
|
}
|
|
243
|
+
// Re-snap to bottom if user was there before heights grew
|
|
244
|
+
if (heightChanged && wasAtBottom) {
|
|
245
|
+
this.scrollToBottom();
|
|
246
|
+
}
|
|
232
247
|
}
|
|
233
248
|
|
|
234
249
|
scrollToBottom(): void {
|
|
235
|
-
this.container.scrollTop = this.
|
|
250
|
+
this.container.scrollTop = this.container.scrollHeight;
|
|
236
251
|
this.scheduleRender();
|
|
237
252
|
}
|
|
238
253
|
|
|
@@ -240,6 +255,7 @@ export class VirtualScroll {
|
|
|
240
255
|
this.items = [];
|
|
241
256
|
this._totalHeight = 0;
|
|
242
257
|
if (this._active) {
|
|
258
|
+
this.container.classList.remove('vs-active');
|
|
243
259
|
this.container.removeEventListener('scroll', this.scrollHandler);
|
|
244
260
|
this.viewport.innerHTML = '';
|
|
245
261
|
this.spacerTop.style.height = '0';
|
package/public/js/ws.ts
CHANGED
|
@@ -264,6 +264,8 @@ export function connect(): void {
|
|
|
264
264
|
getVirtualScroll().clear();
|
|
265
265
|
const el = document.getElementById('chatMessages');
|
|
266
266
|
if (el) el.innerHTML = '';
|
|
267
|
+
// Intentional clear — also wipe IndexedDB cache
|
|
268
|
+
import('./features/idb-cache.js').then(m => m.clearCache()).catch(() => {});
|
|
267
269
|
} else if (msg.type === 'session_reset') {
|
|
268
270
|
addSystemMsg(`${ICONS.refresh} Session reset — history preserved`, 'tool-activity');
|
|
269
271
|
} else if (msg.type === 'agent_added' || msg.type === 'agent_updated' || msg.type === 'agent_deleted') {
|
|
@@ -278,12 +280,16 @@ export function connect(): void {
|
|
|
278
280
|
state.ws.onopen = () => {
|
|
279
281
|
console.log('[ws] connected');
|
|
280
282
|
const now = Date.now();
|
|
281
|
-
const skipReload = now - lastLoadTs <
|
|
283
|
+
const skipReload = now - lastLoadTs < 10000;
|
|
282
284
|
import('./ui.js').then(async m => {
|
|
283
285
|
m.cleanupToolActivity();
|
|
284
286
|
if (!skipReload) {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
+
try {
|
|
288
|
+
await m.loadMessages();
|
|
289
|
+
lastLoadTs = Date.now();
|
|
290
|
+
} catch (error) {
|
|
291
|
+
console.error('[ws] loadMessages failed', error);
|
|
292
|
+
}
|
|
287
293
|
}
|
|
288
294
|
m.setStatus('idle');
|
|
289
295
|
});
|
package/public/locales/en.json
CHANGED
|
@@ -53,9 +53,8 @@
|
|
|
53
53
|
"cmd.skill.resetDone": "Skill reset completed.",
|
|
54
54
|
"cmd.employee.resetUnavailable": "/employee reset is not available in this environment.",
|
|
55
55
|
"cmd.employee.resetDone": "Employees reset to defaults ({count} agents)",
|
|
56
|
-
"cmd.clear.
|
|
57
|
-
"cmd.clear.
|
|
58
|
-
"cmd.clear.done": "Screen cleared. (Chat history preserved)",
|
|
56
|
+
"cmd.clear.remote": "Conversation history cleared. Starting fresh.",
|
|
57
|
+
"cmd.clear.done": "Conversation history cleared and screen reset.",
|
|
59
58
|
"cmd.reset.confirm": "Full reset: MCP, skills, employees, and session will be reset to defaults.\nType /reset confirm to proceed.",
|
|
60
59
|
"cmd.reset.unavailable": "Reset is not supported in this environment.",
|
|
61
60
|
"cmd.reset.done": "Reset complete: {items}",
|
package/public/locales/ko.json
CHANGED
|
@@ -53,9 +53,8 @@
|
|
|
53
53
|
"cmd.skill.resetDone": "스킬 초기화를 실행했습니다.",
|
|
54
54
|
"cmd.employee.resetUnavailable": "이 환경에서는 /employee reset을 사용할 수 없습니다.",
|
|
55
55
|
"cmd.employee.resetDone": "직원 기본값으로 재설정 완료 ({count}명)",
|
|
56
|
-
"cmd.clear.
|
|
57
|
-
"cmd.clear.
|
|
58
|
-
"cmd.clear.done": "화면을 정리했습니다. (대화 기록은 유지됨)",
|
|
56
|
+
"cmd.clear.remote": "대화 히스토리를 초기화했습니다. 새 대화를 시작합니다.",
|
|
57
|
+
"cmd.clear.done": "대화 히스토리를 초기화하고 화면을 정리했습니다.",
|
|
59
58
|
"cmd.reset.confirm": "전체 초기화: MCP, 스킬, 직원, 세션을 기본값으로 재설정합니다.\n실행하려면 /reset confirm 을 입력하세요.",
|
|
60
59
|
"cmd.reset.unavailable": "이 환경에서는 초기화가 지원되지 않습니다.",
|
|
61
60
|
"cmd.reset.done": "초기화 완료: {items}",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{X as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as createArchitectureServices};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{M as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{j as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{A as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{k as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{O as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{D as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as render};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{E as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as render};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{T as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{w as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{C as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{S as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{x as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{b as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{y as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{J as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as createGitGraphServices};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{v as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{K as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as createInfoServices};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{_ as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{g as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{h as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{m as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{t as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{p as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{W as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as createPacketServices};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{H as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as createPieServices};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{f as e}from"./vendor-mermaid-DV2i2BfY.js";export{e as diagram};
|