@pixelbyte-software/pixcode 1.38.0 → 1.38.1

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 (48) hide show
  1. package/dist/assets/{index-C-gVa0Gf.js → index-Br191izN.js} +139 -139
  2. package/dist/assets/index-BzL2G4Sw.css +32 -0
  3. package/dist/index.html +2 -2
  4. package/dist-server/server/database/db.js +18 -3
  5. package/dist-server/server/database/db.js.map +1 -1
  6. package/dist-server/server/index.js +6 -0
  7. package/dist-server/server/index.js.map +1 -1
  8. package/dist-server/server/modules/providers/provider.routes.js +107 -0
  9. package/dist-server/server/modules/providers/provider.routes.js.map +1 -1
  10. package/dist-server/server/routes/auth.js +15 -0
  11. package/dist-server/server/routes/auth.js.map +1 -1
  12. package/dist-server/server/routes/diagnostics.js +26 -3
  13. package/dist-server/server/routes/diagnostics.js.map +1 -1
  14. package/dist-server/server/routes/public-api.js +16 -0
  15. package/dist-server/server/routes/public-api.js.map +1 -0
  16. package/dist-server/server/routes/remote.js +26 -0
  17. package/dist-server/server/routes/remote.js.map +1 -0
  18. package/dist-server/server/routes/settings.js +23 -2
  19. package/dist-server/server/routes/settings.js.map +1 -1
  20. package/dist-server/server/routes/taskmaster.js +102 -2
  21. package/dist-server/server/routes/taskmaster.js.map +1 -1
  22. package/dist-server/server/services/diagnostics.js +52 -1
  23. package/dist-server/server/services/diagnostics.js.map +1 -1
  24. package/dist-server/server/services/public-api-manifest.js +83 -0
  25. package/dist-server/server/services/public-api-manifest.js.map +1 -0
  26. package/dist-server/server/services/remote-connection.js +120 -0
  27. package/dist-server/server/services/remote-connection.js.map +1 -0
  28. package/dist-server/server/services/telegram/control-center.js +62 -2
  29. package/dist-server/server/services/telegram/control-center.js.map +1 -1
  30. package/dist-server/server/services/telegram/translations.js +16 -4
  31. package/dist-server/server/services/telegram/translations.js.map +1 -1
  32. package/package.json +2 -1
  33. package/scripts/smoke/v138-completion.mjs +132 -0
  34. package/server/database/db.js +21 -3
  35. package/server/index.js +8 -0
  36. package/server/modules/providers/provider.routes.ts +134 -0
  37. package/server/routes/auth.js +20 -1
  38. package/server/routes/diagnostics.js +29 -3
  39. package/server/routes/public-api.js +21 -0
  40. package/server/routes/remote.js +33 -0
  41. package/server/routes/settings.js +25 -2
  42. package/server/routes/taskmaster.js +103 -2
  43. package/server/services/diagnostics.js +61 -1
  44. package/server/services/public-api-manifest.js +87 -0
  45. package/server/services/remote-connection.js +127 -0
  46. package/server/services/telegram/control-center.js +66 -2
  47. package/server/services/telegram/translations.js +16 -4
  48. package/dist/assets/index-CfHK8y_H.css +0 -32
@@ -0,0 +1,127 @@
1
+ import { appConfigDb } from '../database/db.js';
2
+
3
+ const CONFIG_KEY = 'remote_connection';
4
+
5
+ const DEFAULT_CONFIG = {
6
+ mode: 'local',
7
+ remoteUrl: null,
8
+ apiKey: null,
9
+ updatedAt: null,
10
+ lastHealth: null,
11
+ };
12
+
13
+ function parseStoredConfig() {
14
+ const raw = appConfigDb.get(CONFIG_KEY);
15
+ if (!raw) return DEFAULT_CONFIG;
16
+ try {
17
+ return normalizeRemoteConnectionConfig(JSON.parse(raw));
18
+ } catch {
19
+ return DEFAULT_CONFIG;
20
+ }
21
+ }
22
+
23
+ function normalizeRemoteUrl(value) {
24
+ const raw = typeof value === 'string' ? value.trim() : '';
25
+ if (!raw) return null;
26
+
27
+ const parsed = new URL(raw);
28
+ if (!['http:', 'https:'].includes(parsed.protocol)) {
29
+ throw new Error('Remote Pixcode URL must use http or https.');
30
+ }
31
+ parsed.pathname = parsed.pathname.replace(/\/+$/, '');
32
+ parsed.search = '';
33
+ parsed.hash = '';
34
+ return parsed.toString().replace(/\/$/, '');
35
+ }
36
+
37
+ export function normalizeRemoteConnectionConfig(input = {}) {
38
+ const mode = input.mode === 'remote' ? 'remote' : 'local';
39
+ const remoteUrl = mode === 'remote' ? normalizeRemoteUrl(input.remoteUrl) : null;
40
+ const apiKey = mode === 'remote' && typeof input.apiKey === 'string' && input.apiKey.trim()
41
+ ? input.apiKey.trim()
42
+ : null;
43
+
44
+ if (mode === 'remote' && !remoteUrl) {
45
+ throw new Error('Remote Pixcode URL is required in remote mode.');
46
+ }
47
+
48
+ return {
49
+ ...DEFAULT_CONFIG,
50
+ ...input,
51
+ mode,
52
+ remoteUrl,
53
+ apiKey,
54
+ updatedAt: typeof input.updatedAt === 'string' ? input.updatedAt : new Date().toISOString(),
55
+ lastHealth: input.lastHealth && typeof input.lastHealth === 'object' ? input.lastHealth : null,
56
+ };
57
+ }
58
+
59
+ export function saveRemoteConnectionConfig(input = {}) {
60
+ const current = parseStoredConfig();
61
+ const normalized = normalizeRemoteConnectionConfig({
62
+ ...current,
63
+ ...input,
64
+ apiKey: input.apiKey === undefined ? current.apiKey : input.apiKey,
65
+ updatedAt: new Date().toISOString(),
66
+ });
67
+ appConfigDb.set(CONFIG_KEY, JSON.stringify(normalized));
68
+ return getPublicRemoteConnectionConfig(normalized);
69
+ }
70
+
71
+ export function getRemoteConnectionConfig() {
72
+ return parseStoredConfig();
73
+ }
74
+
75
+ export function getPublicRemoteConnectionConfig(config = parseStoredConfig()) {
76
+ return {
77
+ mode: config.mode,
78
+ remoteUrl: config.remoteUrl,
79
+ apiKeyPresent: Boolean(config.apiKey),
80
+ updatedAt: config.updatedAt,
81
+ lastHealth: config.lastHealth,
82
+ };
83
+ }
84
+
85
+ export async function checkRemoteConnection(config = parseStoredConfig(), { timeoutMs = 5000 } = {}) {
86
+ const normalized = normalizeRemoteConnectionConfig(config);
87
+ if (normalized.mode !== 'remote') {
88
+ const localHealth = {
89
+ reachable: true,
90
+ checkedAt: new Date().toISOString(),
91
+ status: 'local',
92
+ message: 'Pixcode is running in local mode.',
93
+ };
94
+ saveRemoteConnectionConfig({ ...normalized, lastHealth: localHealth });
95
+ return localHealth;
96
+ }
97
+
98
+ const controller = new AbortController();
99
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
100
+ const checkedAt = new Date().toISOString();
101
+ try {
102
+ const response = await fetch(`${normalized.remoteUrl}/api/auth/status`, {
103
+ signal: controller.signal,
104
+ headers: normalized.apiKey ? { 'X-API-Key': normalized.apiKey } : {},
105
+ });
106
+ const health = {
107
+ reachable: response.ok,
108
+ checkedAt,
109
+ status: response.ok ? 'ok' : 'http_error',
110
+ statusCode: response.status,
111
+ message: response.ok ? 'Remote Pixcode server is reachable.' : `Remote server returned HTTP ${response.status}.`,
112
+ };
113
+ saveRemoteConnectionConfig({ ...normalized, lastHealth: health });
114
+ return health;
115
+ } catch (error) {
116
+ const health = {
117
+ reachable: false,
118
+ checkedAt,
119
+ status: error?.name === 'AbortError' ? 'timeout' : 'network_error',
120
+ message: error?.message || 'Remote Pixcode server is unreachable.',
121
+ };
122
+ saveRemoteConnectionConfig({ ...normalized, lastHealth: health });
123
+ return health;
124
+ } finally {
125
+ clearTimeout(timeout);
126
+ }
127
+ }
@@ -49,6 +49,8 @@ const CONTROL_COMMANDS = new Set([
49
49
  '/workflows',
50
50
  '/orchestration',
51
51
  '/runs',
52
+ '/sessions',
53
+ '/newchat',
52
54
  '/tasks',
53
55
  '/task',
54
56
  '/settings',
@@ -213,6 +215,7 @@ function mainMenuKeyboard(lang) {
213
215
  [button(t(lang, 'control.button.projects'), 'projects'), button(t(lang, 'control.button.provider'), 'providers')],
214
216
  [button(t(lang, 'control.button.models'), 'models'), button(t(lang, 'control.button.workflows'), 'workflows')],
215
217
  [button(t(lang, 'control.button.tasks'), 'tasks'), button(t(lang, 'control.button.runs'), 'runs')],
218
+ [button(t(lang, 'control.button.sessions'), 'sessions'), button(t(lang, 'control.button.newChat'), 'new_chat')],
216
219
  [button(t(lang, 'control.button.install'), 'install_menu'), button(t(lang, 'control.button.auth'), 'auth_menu')],
217
220
  [button(t(lang, 'control.button.settings'), 'settings')],
218
221
  ];
@@ -347,6 +350,44 @@ async function showRuns({ bot, chatId, link, editMessageId }) {
347
350
  });
348
351
  }
349
352
 
353
+ async function showSessions({ bot, chatId, link, editMessageId }) {
354
+ const lang = languageFor(link);
355
+ const state = getState(link.user_id);
356
+ if (!state.selectedProjectName) {
357
+ await send(bot, chatId, t(lang, 'control.selectProjectFirst'), { editMessageId });
358
+ return;
359
+ }
360
+
361
+ const data = await localApi(link.user_id, `/api/projects/${encodeURIComponent(state.selectedProjectName)}/sessions?limit=10&offset=0`);
362
+ const sessions = Array.isArray(data?.sessions) ? data.sessions : [];
363
+ if (sessions.length === 0) {
364
+ await send(bot, chatId, t(lang, 'control.noSessions'), { editMessageId });
365
+ return;
366
+ }
367
+
368
+ const lines = sessions.slice(0, 10).map((session, index) =>
369
+ `${index + 1}. ${compact(session.summary || session.id || session.sessionId, 70)}`
370
+ );
371
+ await send(bot, chatId, `${t(lang, 'control.recentSessions')}\n\n${lines.join('\n')}`, {
372
+ editMessageId,
373
+ reply_markup: {
374
+ inline_keyboard: [
375
+ [button(t(lang, 'control.button.newChat'), 'new_chat')],
376
+ [button(t(lang, 'control.button.mainMenu'), 'menu')],
377
+ ],
378
+ },
379
+ });
380
+ }
381
+
382
+ async function startNewChat({ bot, chatId, link, editMessageId }) {
383
+ const lang = languageFor(link);
384
+ updateTelegramControlState(link.user_id, { awaiting: { type: 'agent_prompt' } });
385
+ await send(bot, chatId, t(lang, 'control.newChatReady'), {
386
+ editMessageId,
387
+ reply_markup: { inline_keyboard: [[button(t(lang, 'control.button.mainMenu'), 'menu')]] },
388
+ });
389
+ }
390
+
350
391
  async function showTaskMasterTasks({ bot, chatId, link, editMessageId }) {
351
392
  const lang = languageFor(link);
352
393
  const state = getState(link.user_id);
@@ -483,7 +524,10 @@ function summarizeRun(run, mode) {
483
524
  ];
484
525
  const nodeRuns = Array.isArray(run.nodeRuns) ? run.nodeRuns : [];
485
526
  if (mode !== 'final') {
486
- for (const node of nodeRuns) {
527
+ const visibleNodes = mode === 'errors'
528
+ ? nodeRuns.filter((node) => node.error || node.status === 'failed')
529
+ : nodeRuns;
530
+ for (const node of visibleNodes) {
487
531
  lines.push(`- ${node.status}: ${node.agentLabel || node.nodeId}${node.error ? ` — ${node.error}` : ''}`);
488
532
  }
489
533
  }
@@ -513,6 +557,15 @@ async function monitorWorkflowRun({ bot, chatId, link, runId }) {
513
557
  }
514
558
  }
515
559
  }
560
+ if (state.progressMode === 'errors') {
561
+ for (const node of nodeRuns.filter((candidate) => candidate.error || candidate.status === 'failed')) {
562
+ const key = `${node.nodeId}:${node.status}:${node.error || ''}`;
563
+ if (!seenNodeStatus.has(key)) {
564
+ seenNodeStatus.set(key, true);
565
+ await send(bot, chatId, `${node.agentLabel || node.nodeId}: ${node.status}\n${node.error || ''}`);
566
+ }
567
+ }
568
+ }
516
569
  if (TERMINAL_RUN_STATES.has(run.status)) {
517
570
  await send(bot, chatId, summarizeRun(run, state.progressMode));
518
571
  return;
@@ -664,6 +717,7 @@ async function showSettings({ bot, chatId, link, editMessageId }) {
664
717
  [
665
718
  button(state.progressMode === 'final' ? checked(t(lang, 'control.button.progressFinal')) : t(lang, 'control.button.progressFinal'), 'progress_mode', { progressMode: 'final' }),
666
719
  button(state.progressMode === 'steps' ? checked(t(lang, 'control.button.progressSteps')) : t(lang, 'control.button.progressSteps'), 'progress_mode', { progressMode: 'steps' }),
720
+ button(state.progressMode === 'errors' ? checked(t(lang, 'control.button.progressErrors')) : t(lang, 'control.button.progressErrors'), 'progress_mode', { progressMode: 'errors' }),
667
721
  button(state.progressMode === 'all' ? checked(t(lang, 'control.button.progressAll')) : t(lang, 'control.button.progressAll'), 'progress_mode', { progressMode: 'all' }),
668
722
  ],
669
723
  [
@@ -741,6 +795,14 @@ async function handleCommand({ bot, chatId, link, text }) {
741
795
  await showRuns({ bot, chatId, link });
742
796
  return true;
743
797
  }
798
+ if (command === '/sessions') {
799
+ await showSessions({ bot, chatId, link });
800
+ return true;
801
+ }
802
+ if (command === '/newchat') {
803
+ await startNewChat({ bot, chatId, link });
804
+ return true;
805
+ }
744
806
  if (command === '/tasks') {
745
807
  await showTaskMasterTasks({ bot, chatId, link });
746
808
  return true;
@@ -775,7 +837,7 @@ async function handleCommand({ bot, chatId, link, text }) {
775
837
  return true;
776
838
  }
777
839
  if (command === '/progress') {
778
- if (!['final', 'steps', 'all'].includes(argText)) {
840
+ if (!['final', 'steps', 'all', 'errors'].includes(argText)) {
779
841
  await send(bot, chatId, t(lang, 'control.progressUsage'));
780
842
  return true;
781
843
  }
@@ -877,6 +939,8 @@ export async function handleTelegramControlCallback({ bot, query, link }) {
877
939
  if (action === 'models_refresh') return showModelMenu({ bot, chatId, link, refresh: true, editMessageId });
878
940
  if (action === 'workflows') return showWorkflowMenu({ bot, chatId, link, editMessageId });
879
941
  if (action === 'runs') return showRuns({ bot, chatId, link, editMessageId });
942
+ if (action === 'sessions') return showSessions({ bot, chatId, link, editMessageId });
943
+ if (action === 'new_chat') return startNewChat({ bot, chatId, link, editMessageId });
880
944
  if (action === 'tasks') return showTaskMasterTasks({ bot, chatId, link, editMessageId });
881
945
  if (action === 'install_menu') return showInstallMenu({ bot, chatId, link, editMessageId });
882
946
  if (action === 'auth_menu') return showAuthMenu({ bot, chatId, link, editMessageId });
@@ -18,7 +18,7 @@ const EN = {
18
18
  'bridge.queued': '📨 Message forwarded to your latest session. I will reply when the agent responds.',
19
19
  'bridge.disabled': 'Message bridge is disabled. Enable it in Pixcode → Settings → Telegram.',
20
20
  'control.menu': 'Pixcode Telegram control center',
21
- 'control.help': 'Commands: /menu, /projects, /provider, /model, /workflows, /runs, /tasks, /task <id>, /chat <prompt>, /workflow <prompt>, /install, /auth, /settings, /progress final|steps|all, /control on|off.',
21
+ 'control.help': 'Commands: /menu, /projects, /provider, /model, /workflows, /runs, /sessions, /newchat, /tasks, /task <id>, /chat <prompt>, /workflow <prompt>, /install, /auth, /settings, /progress final|steps|errors|all, /control on|off.',
22
22
  'control.examples': 'Examples:\n/chat summarize this project\n/chat fix the last error\n/workflow review the current changes\n/settings to change language and progress mode',
23
23
  'control.unknownCommand': 'I do not know that command yet. Here are the available Pixcode commands:',
24
24
  'control.onboarding': 'How to start:\n/projects to pick a project\n/provider to pick the CLI\n/model to choose the model\n/chat <prompt> to run the current agent\n/workflow <prompt> to run orchestration\n/help to see all commands',
@@ -32,6 +32,8 @@ const EN = {
32
32
  'control.button.models': 'Models',
33
33
  'control.button.workflows': 'Workflows',
34
34
  'control.button.runs': 'Runs',
35
+ 'control.button.sessions': 'Sessions',
36
+ 'control.button.newChat': 'New chat',
35
37
  'control.button.tasks': 'Tasks',
36
38
  'control.button.install': 'CLI install',
37
39
  'control.button.auth': 'Auth help',
@@ -43,6 +45,7 @@ const EN = {
43
45
  'control.button.remoteOff': 'Remote control: off',
44
46
  'control.button.progressFinal': 'Progress: final',
45
47
  'control.button.progressSteps': 'Progress: steps',
48
+ 'control.button.progressErrors': 'Progress: errors',
46
49
  'control.button.progressAll': 'Progress: all',
47
50
  'control.button.language': 'Language',
48
51
  'control.button.mainMenu': 'Main menu',
@@ -55,6 +58,9 @@ const EN = {
55
58
  'control.pickWorkflow': 'Pick an orchestration workflow:',
56
59
  'control.noRuns': 'No orchestration runs yet.',
57
60
  'control.recentRuns': 'Recent orchestration runs:',
61
+ 'control.noSessions': 'No sessions were found for the selected project.',
62
+ 'control.recentSessions': 'Recent sessions:',
63
+ 'control.newChatReady': 'New chat is ready. Send the prompt you want to run.',
58
64
  'control.noTaskMasterTasks': 'No open TaskMaster tasks were found for the selected project.',
59
65
  'control.pickTaskMasterTask': 'Pick a TaskMaster task to run with the selected provider:',
60
66
  'control.disabled': 'Telegram remote control is disabled. Enable it in Pixcode → Settings → Telegram or send /control on.',
@@ -86,7 +92,7 @@ const EN = {
86
92
  'control.sendTaskId': 'Send the TaskMaster task id to run.',
87
93
  'control.remoteEnabled': 'Remote control enabled.',
88
94
  'control.remoteDisabled': 'Remote control disabled.',
89
- 'control.progressUsage': 'Use /progress final, /progress steps, or /progress all.',
95
+ 'control.progressUsage': 'Use /progress final, /progress steps, /progress errors, or /progress all.',
90
96
  'control.progressSet': 'Progress mode set to {{mode}}.',
91
97
  'control.cancelUsage': 'Use /cancel <runId>.',
92
98
  'control.runStatus': 'Run {{runId}} status: {{status}}',
@@ -109,7 +115,7 @@ const TR = {
109
115
  'bridge.queued': '📨 Mesaj son oturumuna iletildi. Ajan cevap verince sana yazacağım.',
110
116
  'bridge.disabled': 'Mesaj köprüsü kapalı. Pixcode → Ayarlar → Telegram\'dan açabilirsin.',
111
117
  'control.menu': 'Pixcode Telegram kontrol merkezi',
112
- 'control.help': 'Komutlar: /menu, /projects, /provider, /model, /workflows, /runs, /tasks, /task <id>, /chat <prompt>, /workflow <prompt>, /install, /auth, /settings, /progress final|steps|all, /control on|off.',
118
+ 'control.help': 'Komutlar: /menu, /projects, /provider, /model, /workflows, /runs, /sessions, /newchat, /tasks, /task <id>, /chat <prompt>, /workflow <prompt>, /install, /auth, /settings, /progress final|steps|errors|all, /control on|off.',
113
119
  'control.examples': 'Örnekler:\n/chat bu projeyi özetle\n/chat son hatayı düzelt\n/workflow mevcut değişiklikleri incele\n/settings ile dil ve ilerleme modunu değiştir',
114
120
  'control.unknownCommand': 'Bu komutu henüz tanımıyorum. Kullanabileceğin Pixcode komutları:',
115
121
  'control.onboarding': 'Nasıl başlarsın:\n/projects ile proje seç\n/provider ile CLI seç\n/model ile modeli seç\n/chat <prompt> ile ajanı çalıştır\n/workflow <prompt> ile orkestrasyon başlat\n/help ile tüm komutları gör',
@@ -123,6 +129,8 @@ const TR = {
123
129
  'control.button.models': 'Modeller',
124
130
  'control.button.workflows': 'İş akışları',
125
131
  'control.button.runs': 'Çalışmalar',
132
+ 'control.button.sessions': 'Oturumlar',
133
+ 'control.button.newChat': 'Yeni sohbet',
126
134
  'control.button.tasks': 'Görevler',
127
135
  'control.button.install': 'CLI kur',
128
136
  'control.button.auth': 'Giriş yardımı',
@@ -134,6 +142,7 @@ const TR = {
134
142
  'control.button.remoteOff': 'Uzaktan kontrol: kapalı',
135
143
  'control.button.progressFinal': 'İlerleme: final',
136
144
  'control.button.progressSteps': 'İlerleme: adımlar',
145
+ 'control.button.progressErrors': 'İlerleme: hatalar',
137
146
  'control.button.progressAll': 'İlerleme: tümü',
138
147
  'control.button.language': 'Dil',
139
148
  'control.button.mainMenu': 'Ana menü',
@@ -146,6 +155,9 @@ const TR = {
146
155
  'control.pickWorkflow': 'Bir orkestrasyon iş akışı seç:',
147
156
  'control.noRuns': 'Henüz orkestrasyon çalışması yok.',
148
157
  'control.recentRuns': 'Son orkestrasyon çalışmaları:',
158
+ 'control.noSessions': 'Seçili proje için oturum bulunamadı.',
159
+ 'control.recentSessions': 'Son oturumlar:',
160
+ 'control.newChatReady': 'Yeni sohbet hazır. Çalıştırmak istediğin promptu gönder.',
149
161
  'control.noTaskMasterTasks': 'Seçili proje için açık TaskMaster görevi bulunamadı.',
150
162
  'control.pickTaskMasterTask': 'Seçili sağlayıcıyla çalıştırılacak TaskMaster görevini seç:',
151
163
  'control.disabled': 'Telegram uzaktan kontrol kapalı. Pixcode → Ayarlar → Telegram içinden aç veya /control on gönder.',
@@ -177,7 +189,7 @@ const TR = {
177
189
  'control.sendTaskId': 'Çalıştırılacak TaskMaster görev id değerini gönder.',
178
190
  'control.remoteEnabled': 'Uzaktan kontrol açıldı.',
179
191
  'control.remoteDisabled': 'Uzaktan kontrol kapatıldı.',
180
- 'control.progressUsage': '/progress final, /progress steps veya /progress all kullan.',
192
+ 'control.progressUsage': '/progress final, /progress steps, /progress errors veya /progress all kullan.',
181
193
  'control.progressSet': 'İlerleme modu {{mode}} olarak ayarlandı.',
182
194
  'control.cancelUsage': '/cancel <runId> kullan.',
183
195
  'control.runStatus': '{{runId}} durumu: {{status}}',