jinzd-ai-cli 0.4.205 → 0.4.207

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.
Files changed (30) hide show
  1. package/README.md +1 -1
  2. package/dist/{batch-RBPABHS7.js → batch-6PORMJWT.js} +2 -2
  3. package/dist/{chunk-XIYSFN4U.js → chunk-2WV6AGHM.js} +1 -1
  4. package/dist/{chunk-43H3D2PK.js → chunk-BQIV4ZQT.js} +1 -1
  5. package/dist/{chunk-JVYH66N2.js → chunk-KJHMRAJU.js} +1 -1
  6. package/dist/{chunk-ZSC4YLEJ.js → chunk-KOU4KX7J.js} +2 -2
  7. package/dist/{chunk-UZISJ3KZ.js → chunk-QMXC327F.js} +3 -3
  8. package/dist/{chunk-3RET7PL7.js → chunk-QNUVZO5X.js} +1 -1
  9. package/dist/{chunk-43MWRT2C.js → chunk-QQYSZMET.js} +20 -17
  10. package/dist/{chunk-VOV2PWZU.js → chunk-SFCNFX42.js} +1 -1
  11. package/dist/{chunk-TZ7SNVSG.js → chunk-TW47X5AO.js} +1 -1
  12. package/dist/{chunk-SMFRJCXB.js → chunk-XPBEJB27.js} +8 -1
  13. package/dist/{ci-ABE74AXH.js → ci-AYMV66P3.js} +9 -9
  14. package/dist/{constants-GMQMJRIO.js → constants-NHGTSHKT.js} +1 -1
  15. package/dist/{doctor-cli-M5PPO3L3.js → doctor-cli-WKH7T4AW.js} +6 -6
  16. package/dist/electron-server.js +46 -41
  17. package/dist/{hub-NCU5JNCK.js → hub-6R3M3NTC.js} +1 -1
  18. package/dist/index.js +102 -98
  19. package/dist/{run-tests-ZFAEFEYK.js → run-tests-5F2OBWIK.js} +2 -2
  20. package/dist/{run-tests-FGRWBNVF.js → run-tests-NBCA4KEG.js} +1 -1
  21. package/dist/{server-RKP7JRRE.js → server-37EXC4EB.js} +5 -5
  22. package/dist/{server-ML6ZL3J5.js → server-L2MXWYF7.js} +26 -26
  23. package/dist/{task-orchestrator-LJP5XZSC.js → task-orchestrator-2DTDVCMR.js} +5 -5
  24. package/dist/{usage-YX2235W5.js → usage-SZWZFX3T.js} +2 -2
  25. package/dist/web/client/actions.js +84 -0
  26. package/dist/web/client/app.js +23 -205
  27. package/dist/web/client/index.html +27 -42
  28. package/dist/web/client/sw.js +1 -1
  29. package/dist/web/client/templates.js +188 -0
  30. package/package.json +1 -1
@@ -36,7 +36,7 @@
36
36
  <p id="auth-subtitle" class="text-sm text-base-content/60 mt-1">Sign in to continue</p>
37
37
  </div>
38
38
  <div id="auth-error" class="alert alert-error text-sm mb-4 hidden"></div>
39
- <form id="auth-form" onsubmit="handleAuth(event)">
39
+ <form id="auth-form" >
40
40
  <div class="form-control mb-3">
41
41
  <label class="label"><span class="label-text">Username</span></label>
42
42
  <input id="auth-username" type="text" class="input input-bordered w-full" placeholder="username" autocomplete="username" required minlength="2" maxlength="32">
@@ -48,7 +48,7 @@
48
48
  <button id="auth-submit" type="submit" class="btn btn-primary w-full">Sign In</button>
49
49
  </form>
50
50
  <div class="text-center mt-4">
51
- <button id="auth-toggle" class="btn btn-ghost btn-sm" onclick="toggleAuthMode()">
51
+ <button id="auth-toggle" class="btn btn-ghost btn-sm" data-action="toggle-auth-mode" type="button">
52
52
  Don't have an account? <span class="text-primary">Register</span>
53
53
  </button>
54
54
  </div>
@@ -76,18 +76,18 @@
76
76
  <div class="dropdown dropdown-end">
77
77
  <div tabindex="0" role="button" class="btn btn-sm btn-ghost">🎨</div>
78
78
  <ul tabindex="0" class="dropdown-content menu bg-base-200 rounded-box z-10 w-40 p-2 shadow-lg border border-base-content/10">
79
- <li><a onclick="setTheme('dark')">🌙 Dark</a></li>
80
- <li><a onclick="setTheme('night')">🌃 Night</a></li>
81
- <li><a onclick="setTheme('dim')">🌑 Dim</a></li>
82
- <li><a onclick="setTheme('synthwave')">🎵 Synthwave</a></li>
83
- <li><a onclick="setTheme('cyberpunk')">🤖 Cyberpunk</a></li>
84
- <li><a onclick="setTheme('light')">☀️ Light</a></li>
85
- <li><a onclick="setTheme('cupcake')">🧁 Cupcake</a></li>
86
- <li><a onclick="setTheme('nord')">❄️ Nord</a></li>
79
+ <li><a data-theme-choice="dark">🌙 Dark</a></li>
80
+ <li><a data-theme-choice="night">🌃 Night</a></li>
81
+ <li><a data-theme-choice="dim">🌑 Dim</a></li>
82
+ <li><a data-theme-choice="synthwave">🎵 Synthwave</a></li>
83
+ <li><a data-theme-choice="cyberpunk">🤖 Cyberpunk</a></li>
84
+ <li><a data-theme-choice="light">☀️ Light</a></li>
85
+ <li><a data-theme-choice="cupcake">🧁 Cupcake</a></li>
86
+ <li><a data-theme-choice="nord">❄️ Nord</a></li>
87
87
  </ul>
88
88
  </div>
89
89
  <!-- Enable Auth button (shown when no users registered) -->
90
- <button id="btn-enable-auth" class="btn btn-sm btn-ghost hidden" title="Enable multi-user auth" onclick="showEnableAuthDialog()">👤 Enable Auth</button>
90
+ <button id="btn-enable-auth" class="btn btn-sm btn-ghost hidden" title="Enable multi-user auth" data-action="show-enable-auth">👤 Enable Auth</button>
91
91
  <!-- User menu (shown when authenticated) -->
92
92
  <div id="user-menu" class="dropdown dropdown-end hidden">
93
93
  <div tabindex="0" role="button" class="btn btn-sm btn-ghost gap-1">
@@ -95,7 +95,7 @@
95
95
  </div>
96
96
  <ul tabindex="0" class="dropdown-content menu bg-base-200 rounded-box z-10 w-40 p-2 shadow-lg border border-base-content/10">
97
97
  <li><a id="user-label" class="text-sm font-semibold pointer-events-none"></a></li>
98
- <li><a onclick="handleLogout()">🚪 Logout</a></li>
98
+ <li><a data-action="logout">🚪 Logout</a></li>
99
99
  </ul>
100
100
  </div>
101
101
  </div>
@@ -119,16 +119,16 @@
119
119
  <div id="tab-sessions" class="sidebar-tab-content flex flex-col flex-1 overflow-hidden">
120
120
  <div class="p-2 border-b border-base-content/10 flex items-center justify-between gap-1">
121
121
  <input id="session-search" type="text" class="input input-xs input-bordered flex-1 min-w-0" placeholder="Search...">
122
- <button id="btn-batch-select" class="btn btn-xs btn-ghost flex-shrink-0" title="Select multiple" onclick="toggleBatchSelect()">☑</button>
122
+ <button id="btn-batch-select" class="btn btn-xs btn-ghost flex-shrink-0" title="Select multiple" data-action="toggle-batch-select">☑</button>
123
123
  <button id="btn-new-session" class="btn btn-xs btn-primary btn-outline flex-shrink-0 whitespace-nowrap" title="New session">+ New</button>
124
124
  </div>
125
125
  <!-- Batch action bar (hidden by default) -->
126
126
  <div id="batch-bar" class="hidden px-2 py-1 border-b border-base-content/10 flex items-center gap-1 bg-base-300 text-xs">
127
127
  <span class="batch-count opacity-60">0 selected</span>
128
128
  <span class="flex-1"></span>
129
- <button class="btn btn-xs btn-ghost" onclick="selectAllSessions()">All</button>
130
- <button class="btn btn-xs btn-error btn-outline" onclick="batchDeleteSelected()">🗑 Delete</button>
131
- <button class="btn btn-xs btn-ghost" onclick="toggleBatchSelect()">Cancel</button>
129
+ <button class="btn btn-xs btn-ghost" data-action="select-all-sessions">All</button>
130
+ <button class="btn btn-xs btn-error btn-outline" data-action="batch-delete-selected">🗑 Delete</button>
131
+ <button class="btn btn-xs btn-ghost" data-action="toggle-batch-select">Cancel</button>
132
132
  </div>
133
133
  <div id="session-list" class="flex-1 overflow-y-auto p-2 flex flex-col gap-1 text-sm">
134
134
  <div class="text-xs opacity-40 text-center py-4">No sessions yet</div>
@@ -216,8 +216,8 @@
216
216
  <label class="flex items-center gap-2 text-xs cursor-pointer">
217
217
  <input id="hub-lanes" type="checkbox" class="checkbox checkbox-xs"> Lanes view (one column per agent)
218
218
  </label>
219
- <button id="hub-start-btn" class="btn btn-primary btn-sm w-full" onclick="startHub()">🏛 Start discussion</button>
220
- <button id="hub-abort-btn" class="btn btn-error btn-outline btn-xs w-full hidden" onclick="abortHub()">⏹ Stop</button>
219
+ <button id="hub-start-btn" class="btn btn-primary btn-sm w-full" data-action="start-hub">🏛 Start discussion</button>
220
+ <button id="hub-abort-btn" class="btn btn-error btn-outline btn-xs w-full hidden" data-action="abort-hub">⏹ Stop</button>
221
221
  <div id="hub-status" class="text-xs opacity-60"></div>
222
222
  </div>
223
223
  </div>
@@ -225,7 +225,7 @@
225
225
  <!-- Sidebar resize handle -->
226
226
  <div id="sidebar-resize" class="sidebar-resize-handle" title="Drag to resize sidebar"></div>
227
227
  <!-- Sidebar backdrop (mobile overlay) -->
228
- <div id="sidebar-backdrop" class="sidebar-backdrop hidden" onclick="closeSidebar()"></div>
228
+ <div id="sidebar-backdrop" class="sidebar-backdrop hidden" data-action="close-sidebar"></div>
229
229
 
230
230
  <!-- Chat Area -->
231
231
  <div class="flex-1 flex flex-col overflow-hidden">
@@ -293,9 +293,9 @@
293
293
  <div class="flex items-center justify-between mb-3">
294
294
  <h3 class="font-bold text-lg">📝 Prompt Templates</h3>
295
295
  <div class="flex gap-1">
296
- <button class="btn btn-xs btn-ghost" onclick="importTemplates()" title="Import">📥 Import</button>
297
- <button class="btn btn-xs btn-ghost" onclick="exportTemplates()" title="Export">📤 Export</button>
298
- <button class="btn btn-xs btn-ghost" onclick="showAddTemplate()" title="New template">+ New</button>
296
+ <button class="btn btn-xs btn-ghost" data-action="import-templates" title="Import">📥 Import</button>
297
+ <button class="btn btn-xs btn-ghost" data-action="export-templates" title="Export">📤 Export</button>
298
+ <button class="btn btn-xs btn-ghost" data-action="show-add-template" title="New template">+ New</button>
299
299
  </div>
300
300
  </div>
301
301
 
@@ -305,8 +305,8 @@
305
305
  <textarea id="tpl-content" class="textarea textarea-bordered w-full text-sm mb-2" rows="4" placeholder="Prompt content..."></textarea>
306
306
  <input id="tpl-tags" type="text" class="input input-sm input-bordered w-full mb-2" placeholder="Tags (comma-separated, optional)">
307
307
  <div class="flex gap-2 justify-end">
308
- <button class="btn btn-sm btn-ghost" onclick="cancelTemplateForm()">Cancel</button>
309
- <button class="btn btn-sm btn-primary" onclick="saveTemplateForm()">Save</button>
308
+ <button class="btn btn-sm btn-ghost" data-action="cancel-template-form">Cancel</button>
309
+ <button class="btn btn-sm btn-primary" data-action="save-template-form">Save</button>
310
310
  </div>
311
311
  <input type="hidden" id="tpl-edit-id">
312
312
  </div>
@@ -338,23 +338,8 @@
338
338
  <form method="dialog" class="modal-backdrop"><button>close</button></form>
339
339
  </dialog>
340
340
 
341
- <script src="app.js"></script>
342
- <script>
343
- // Self-updating service worker (v0.4.159). A new SW postMessages
344
- // 'sw-updated' (and triggers controllerchange) when it takes over; we
345
- // reload once so the user never gets stuck on a stale index.html/app.js
346
- // after upgrading aicli. index.html/app.js are also network-only in sw.js.
347
- if ('serviceWorker' in navigator) {
348
- let swReloaded = false;
349
- const reloadOnce = () => { if (!swReloaded) { swReloaded = true; location.reload(); } };
350
- navigator.serviceWorker.addEventListener('message', (e) => {
351
- if (e.data && e.data.type === 'sw-updated') reloadOnce();
352
- });
353
- navigator.serviceWorker.addEventListener('controllerchange', reloadOnce);
354
- navigator.serviceWorker.register('sw.js').then((reg) => {
355
- reg.update();
356
- }).catch(() => {});
357
- }
358
- </script>
341
+ <script src="app.js"></script>
342
+ <script src="templates.js"></script>
343
+ <script src="actions.js"></script>
359
344
  </body>
360
345
  </html>
@@ -16,7 +16,7 @@ const CACHE_NAME = 'aicli-v3';
16
16
  // HTML/JS that must always be fresh — never served from the SW cache, so an
17
17
  // upgraded server is reflected on the very next load. Other assets (vendored
18
18
  // CSS/JS, icons) are large + content-stable and fine to cache.
19
- const NEVER_CACHE = new Set(['/', '/index.html', '/app.js']);
19
+ const NEVER_CACHE = new Set(['/', '/index.html', '/app.js', '/templates.js', '/actions.js']);
20
20
 
21
21
  // App shell to precache (offline-capable startup). Deliberately EXCLUDES the
22
22
  // always-fresh HTML/JS in NEVER_CACHE — only large content-stable assets.
@@ -0,0 +1,188 @@
1
+ /**
2
+ * Prompt template modal and import/export helpers.
3
+ * Loaded after app.js so it can use shared UI helpers.
4
+ */
5
+
6
+ const TEMPLATES_KEY = 'aicli-templates';
7
+
8
+ function loadTemplates() {
9
+ try {
10
+ return JSON.parse(localStorage.getItem(TEMPLATES_KEY) || '[]');
11
+ } catch {
12
+ return [];
13
+ }
14
+ }
15
+
16
+ function saveTemplatesToStorage(templates) {
17
+ localStorage.setItem(TEMPLATES_KEY, JSON.stringify(templates));
18
+ }
19
+
20
+ function openTemplatesModal() {
21
+ const modal = document.getElementById('templates-modal');
22
+ if (!modal) return;
23
+ cancelTemplateForm();
24
+ renderTemplateList();
25
+ document.getElementById('template-search').value = '';
26
+ modal.showModal();
27
+ }
28
+
29
+ function renderTemplateList(filter) {
30
+ const listEl = document.getElementById('template-list');
31
+ if (!listEl) return;
32
+ const templates = loadTemplates();
33
+ const q = (filter || '').toLowerCase();
34
+ const filtered = q
35
+ ? templates.filter(t => t.name.toLowerCase().includes(q) || (t.tags || []).some(tag => tag.toLowerCase().includes(q)) || t.content.toLowerCase().includes(q))
36
+ : templates;
37
+
38
+ if (filtered.length === 0) {
39
+ listEl.innerHTML = `<div class="template-empty">${q ? 'No matching templates' : 'No templates yet. Click + New to create one.'}</div>`;
40
+ return;
41
+ }
42
+
43
+ listEl.innerHTML = filtered.map(t => {
44
+ const preview = t.content.length > 80 ? t.content.slice(0, 80) + '...' : t.content;
45
+ const tagsHtml = (t.tags || []).map(tag => `<span class="template-item-tag">${escapeHtml(tag)}</span>`).join('');
46
+ return `
47
+ <div class="template-item" data-tpl-id="${t.id}" data-action="use-template">
48
+ <div class="template-item-body">
49
+ <div class="template-item-name">${escapeHtml(t.name)}</div>
50
+ <div class="template-item-preview">${escapeHtml(preview)}</div>
51
+ ${tagsHtml ? `<div class="template-item-tags">${tagsHtml}</div>` : ''}
52
+ </div>
53
+ <div class="template-item-actions">
54
+ <button class="btn btn-xs btn-ghost" data-action="edit-template" title="Edit">Edit</button>
55
+ <button class="btn btn-xs btn-ghost" data-action="delete-template" title="Delete">Delete</button>
56
+ </div>
57
+ </div>`;
58
+ }).join('');
59
+ }
60
+
61
+ function useTemplate(id) {
62
+ const templates = loadTemplates();
63
+ const tpl = templates.find(t => t.id === id);
64
+ if (!tpl) return;
65
+ const input = document.getElementById('user-input');
66
+ if (!input) return;
67
+ input.value = tpl.content;
68
+ input.focus();
69
+ input.style.height = 'auto';
70
+ input.style.height = Math.min(input.scrollHeight, 200) + 'px';
71
+ document.getElementById('templates-modal')?.close();
72
+ }
73
+
74
+ function showAddTemplate() {
75
+ const form = document.getElementById('template-form');
76
+ form.classList.remove('hidden');
77
+ document.getElementById('tpl-edit-id').value = '';
78
+ document.getElementById('tpl-name').value = '';
79
+ document.getElementById('tpl-content').value = '';
80
+ document.getElementById('tpl-tags').value = '';
81
+ document.getElementById('tpl-name').focus();
82
+ }
83
+
84
+ function editTemplate(id) {
85
+ const templates = loadTemplates();
86
+ const tpl = templates.find(t => t.id === id);
87
+ if (!tpl) return;
88
+ const form = document.getElementById('template-form');
89
+ form.classList.remove('hidden');
90
+ document.getElementById('tpl-edit-id').value = tpl.id;
91
+ document.getElementById('tpl-name').value = tpl.name;
92
+ document.getElementById('tpl-content').value = tpl.content;
93
+ document.getElementById('tpl-tags').value = (tpl.tags || []).join(', ');
94
+ document.getElementById('tpl-name').focus();
95
+ }
96
+
97
+ function cancelTemplateForm() {
98
+ document.getElementById('template-form')?.classList.add('hidden');
99
+ }
100
+
101
+ function saveTemplateForm() {
102
+ const name = document.getElementById('tpl-name').value.trim();
103
+ const content = document.getElementById('tpl-content').value.trim();
104
+ const tagsRaw = document.getElementById('tpl-tags').value.trim();
105
+ const editId = document.getElementById('tpl-edit-id').value;
106
+
107
+ if (!name || !content) return;
108
+
109
+ const tags = tagsRaw ? tagsRaw.split(',').map(s => s.trim()).filter(Boolean) : [];
110
+ const templates = loadTemplates();
111
+
112
+ if (editId) {
113
+ const idx = templates.findIndex(t => t.id === editId);
114
+ if (idx >= 0) {
115
+ templates[idx].name = name;
116
+ templates[idx].content = content;
117
+ templates[idx].tags = tags;
118
+ }
119
+ } else {
120
+ templates.unshift({
121
+ id: 'tpl-' + Date.now().toString(36) + Math.random().toString(36).slice(2, 6),
122
+ name,
123
+ content,
124
+ tags,
125
+ createdAt: new Date().toISOString(),
126
+ });
127
+ }
128
+
129
+ saveTemplatesToStorage(templates);
130
+ cancelTemplateForm();
131
+ renderTemplateList();
132
+ }
133
+
134
+ function deleteTemplate(id) {
135
+ const templates = loadTemplates().filter(t => t.id !== id);
136
+ saveTemplatesToStorage(templates);
137
+ renderTemplateList(document.getElementById('template-search')?.value);
138
+ }
139
+
140
+ function exportTemplates() {
141
+ const templates = loadTemplates();
142
+ if (templates.length === 0) return;
143
+ const blob = new Blob([JSON.stringify(templates, null, 2)], { type: 'application/json' });
144
+ const url = URL.createObjectURL(blob);
145
+ const a = document.createElement('a');
146
+ a.href = url;
147
+ a.download = `aicli-templates-${new Date().toISOString().slice(0, 10)}.json`;
148
+ a.click();
149
+ URL.revokeObjectURL(url);
150
+ }
151
+
152
+ function importTemplates() {
153
+ const input = document.createElement('input');
154
+ input.type = 'file';
155
+ input.accept = '.json';
156
+ input.addEventListener('change', () => {
157
+ const file = input.files[0];
158
+ if (!file) return;
159
+ const reader = new FileReader();
160
+ reader.addEventListener('load', () => {
161
+ try {
162
+ const imported = JSON.parse(reader.result);
163
+ if (!Array.isArray(imported)) throw new Error('Not an array');
164
+ const existing = loadTemplates();
165
+ const existingIds = new Set(existing.map(t => t.id));
166
+ let added = 0;
167
+ for (const t of imported) {
168
+ if (t.id && t.name && t.content && !existingIds.has(t.id)) {
169
+ existing.push(t);
170
+ added++;
171
+ }
172
+ }
173
+ saveTemplatesToStorage(existing);
174
+ renderTemplateList();
175
+ addInfoMessage(`Imported ${added} template(s).`);
176
+ } catch {
177
+ addErrorMessage('Failed to import templates: invalid JSON format.');
178
+ }
179
+ });
180
+ reader.readAsText(file);
181
+ });
182
+ input.click();
183
+ }
184
+
185
+ document.getElementById('btn-templates')?.addEventListener('click', openTemplatesModal);
186
+ document.getElementById('template-search')?.addEventListener('input', (e) => {
187
+ renderTemplateList(e.target.value);
188
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jinzd-ai-cli",
3
- "version": "0.4.205",
3
+ "version": "0.4.207",
4
4
  "description": "Cross-platform REPL-style AI CLI with multi-provider support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",