@jonit-dev/night-watch-cli 1.8.10-beta.0 → 1.8.10-beta.2

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 (238) hide show
  1. package/dist/cli.js +2399 -1364
  2. package/dist/scripts/night-watch-helpers.sh +26 -0
  3. package/dist/scripts/night-watch-pr-reviewer-cron.sh +6 -8
  4. package/dist/scripts/night-watch-qa-cron.sh +3 -3
  5. package/dist/web/assets/index-BUgI2S1s.js +406 -0
  6. package/dist/web/assets/index-CkdLFBd7.js +406 -0
  7. package/dist/web/assets/index-RMfswANB.css +1 -0
  8. package/dist/web/index.html +2 -2
  9. package/package.json +1 -1
  10. package/dist/cli.d.ts +0 -3
  11. package/dist/cli.d.ts.map +0 -1
  12. package/dist/cli.js.map +0 -1
  13. package/dist/commands/analytics.d.ts +0 -14
  14. package/dist/commands/analytics.d.ts.map +0 -1
  15. package/dist/commands/analytics.js +0 -69
  16. package/dist/commands/analytics.js.map +0 -1
  17. package/dist/commands/audit.d.ts +0 -19
  18. package/dist/commands/audit.d.ts.map +0 -1
  19. package/dist/commands/audit.js +0 -144
  20. package/dist/commands/audit.js.map +0 -1
  21. package/dist/commands/board.d.ts +0 -9
  22. package/dist/commands/board.d.ts.map +0 -1
  23. package/dist/commands/board.js +0 -702
  24. package/dist/commands/board.js.map +0 -1
  25. package/dist/commands/cancel.d.ts +0 -46
  26. package/dist/commands/cancel.d.ts.map +0 -1
  27. package/dist/commands/cancel.js +0 -239
  28. package/dist/commands/cancel.js.map +0 -1
  29. package/dist/commands/cron.d.ts +0 -8
  30. package/dist/commands/cron.d.ts.map +0 -1
  31. package/dist/commands/cron.js +0 -134
  32. package/dist/commands/cron.js.map +0 -1
  33. package/dist/commands/dashboard/tab-actions.d.ts +0 -10
  34. package/dist/commands/dashboard/tab-actions.d.ts.map +0 -1
  35. package/dist/commands/dashboard/tab-actions.js +0 -247
  36. package/dist/commands/dashboard/tab-actions.js.map +0 -1
  37. package/dist/commands/dashboard/tab-config.d.ts +0 -21
  38. package/dist/commands/dashboard/tab-config.d.ts.map +0 -1
  39. package/dist/commands/dashboard/tab-config.js +0 -873
  40. package/dist/commands/dashboard/tab-config.js.map +0 -1
  41. package/dist/commands/dashboard/tab-logs.d.ts +0 -10
  42. package/dist/commands/dashboard/tab-logs.d.ts.map +0 -1
  43. package/dist/commands/dashboard/tab-logs.js +0 -202
  44. package/dist/commands/dashboard/tab-logs.js.map +0 -1
  45. package/dist/commands/dashboard/tab-schedules.d.ts +0 -21
  46. package/dist/commands/dashboard/tab-schedules.d.ts.map +0 -1
  47. package/dist/commands/dashboard/tab-schedules.js +0 -320
  48. package/dist/commands/dashboard/tab-schedules.js.map +0 -1
  49. package/dist/commands/dashboard/tab-status.d.ts +0 -32
  50. package/dist/commands/dashboard/tab-status.d.ts.map +0 -1
  51. package/dist/commands/dashboard/tab-status.js +0 -424
  52. package/dist/commands/dashboard/tab-status.js.map +0 -1
  53. package/dist/commands/dashboard/types.d.ts +0 -42
  54. package/dist/commands/dashboard/types.d.ts.map +0 -1
  55. package/dist/commands/dashboard/types.js +0 -5
  56. package/dist/commands/dashboard/types.js.map +0 -1
  57. package/dist/commands/dashboard.d.ts +0 -11
  58. package/dist/commands/dashboard.d.ts.map +0 -1
  59. package/dist/commands/dashboard.js +0 -242
  60. package/dist/commands/dashboard.js.map +0 -1
  61. package/dist/commands/doctor.d.ts +0 -16
  62. package/dist/commands/doctor.d.ts.map +0 -1
  63. package/dist/commands/doctor.js +0 -195
  64. package/dist/commands/doctor.js.map +0 -1
  65. package/dist/commands/history.d.ts +0 -7
  66. package/dist/commands/history.d.ts.map +0 -1
  67. package/dist/commands/history.js +0 -49
  68. package/dist/commands/history.js.map +0 -1
  69. package/dist/commands/init.d.ts +0 -45
  70. package/dist/commands/init.d.ts.map +0 -1
  71. package/dist/commands/init.js +0 -777
  72. package/dist/commands/init.js.map +0 -1
  73. package/dist/commands/install.d.ts +0 -65
  74. package/dist/commands/install.d.ts.map +0 -1
  75. package/dist/commands/install.js +0 -405
  76. package/dist/commands/install.js.map +0 -1
  77. package/dist/commands/logs.d.ts +0 -15
  78. package/dist/commands/logs.d.ts.map +0 -1
  79. package/dist/commands/logs.js +0 -155
  80. package/dist/commands/logs.js.map +0 -1
  81. package/dist/commands/merge.d.ts +0 -26
  82. package/dist/commands/merge.d.ts.map +0 -1
  83. package/dist/commands/merge.js +0 -159
  84. package/dist/commands/merge.js.map +0 -1
  85. package/dist/commands/notify.d.ts +0 -7
  86. package/dist/commands/notify.d.ts.map +0 -1
  87. package/dist/commands/notify.js +0 -43
  88. package/dist/commands/notify.js.map +0 -1
  89. package/dist/commands/plan.d.ts +0 -19
  90. package/dist/commands/plan.d.ts.map +0 -1
  91. package/dist/commands/plan.js +0 -88
  92. package/dist/commands/plan.js.map +0 -1
  93. package/dist/commands/prd-state.d.ts +0 -12
  94. package/dist/commands/prd-state.d.ts.map +0 -1
  95. package/dist/commands/prd-state.js +0 -47
  96. package/dist/commands/prd-state.js.map +0 -1
  97. package/dist/commands/prd.d.ts +0 -18
  98. package/dist/commands/prd.d.ts.map +0 -1
  99. package/dist/commands/prd.js +0 -363
  100. package/dist/commands/prd.js.map +0 -1
  101. package/dist/commands/prds.d.ts +0 -13
  102. package/dist/commands/prds.d.ts.map +0 -1
  103. package/dist/commands/prds.js +0 -194
  104. package/dist/commands/prds.js.map +0 -1
  105. package/dist/commands/prs.d.ts +0 -14
  106. package/dist/commands/prs.d.ts.map +0 -1
  107. package/dist/commands/prs.js +0 -104
  108. package/dist/commands/prs.js.map +0 -1
  109. package/dist/commands/qa.d.ts +0 -34
  110. package/dist/commands/qa.d.ts.map +0 -1
  111. package/dist/commands/qa.js +0 -214
  112. package/dist/commands/qa.js.map +0 -1
  113. package/dist/commands/queue.d.ts +0 -8
  114. package/dist/commands/queue.d.ts.map +0 -1
  115. package/dist/commands/queue.js +0 -363
  116. package/dist/commands/queue.js.map +0 -1
  117. package/dist/commands/resolve.d.ts +0 -26
  118. package/dist/commands/resolve.d.ts.map +0 -1
  119. package/dist/commands/resolve.js +0 -186
  120. package/dist/commands/resolve.js.map +0 -1
  121. package/dist/commands/retry.d.ts +0 -9
  122. package/dist/commands/retry.d.ts.map +0 -1
  123. package/dist/commands/retry.js +0 -71
  124. package/dist/commands/retry.js.map +0 -1
  125. package/dist/commands/review.d.ts +0 -82
  126. package/dist/commands/review.d.ts.map +0 -1
  127. package/dist/commands/review.js +0 -479
  128. package/dist/commands/review.js.map +0 -1
  129. package/dist/commands/run.d.ts +0 -73
  130. package/dist/commands/run.d.ts.map +0 -1
  131. package/dist/commands/run.js +0 -509
  132. package/dist/commands/run.js.map +0 -1
  133. package/dist/commands/serve.d.ts +0 -19
  134. package/dist/commands/serve.d.ts.map +0 -1
  135. package/dist/commands/serve.js +0 -142
  136. package/dist/commands/serve.js.map +0 -1
  137. package/dist/commands/shared/env-builder.d.ts +0 -49
  138. package/dist/commands/shared/env-builder.d.ts.map +0 -1
  139. package/dist/commands/shared/env-builder.js +0 -150
  140. package/dist/commands/shared/env-builder.js.map +0 -1
  141. package/dist/commands/slice.d.ts +0 -35
  142. package/dist/commands/slice.d.ts.map +0 -1
  143. package/dist/commands/slice.js +0 -316
  144. package/dist/commands/slice.js.map +0 -1
  145. package/dist/commands/state.d.ts +0 -8
  146. package/dist/commands/state.d.ts.map +0 -1
  147. package/dist/commands/state.js +0 -54
  148. package/dist/commands/state.js.map +0 -1
  149. package/dist/commands/status.d.ts +0 -14
  150. package/dist/commands/status.d.ts.map +0 -1
  151. package/dist/commands/status.js +0 -297
  152. package/dist/commands/status.js.map +0 -1
  153. package/dist/commands/summary.d.ts +0 -14
  154. package/dist/commands/summary.d.ts.map +0 -1
  155. package/dist/commands/summary.js +0 -193
  156. package/dist/commands/summary.js.map +0 -1
  157. package/dist/commands/uninstall.d.ts +0 -25
  158. package/dist/commands/uninstall.d.ts.map +0 -1
  159. package/dist/commands/uninstall.js +0 -134
  160. package/dist/commands/uninstall.js.map +0 -1
  161. package/dist/commands/update.d.ts +0 -22
  162. package/dist/commands/update.d.ts.map +0 -1
  163. package/dist/commands/update.js +0 -90
  164. package/dist/commands/update.js.map +0 -1
  165. package/dist/web/assets/index-2JY0x_Ij.js +0 -381
  166. package/dist/web/assets/index-3h8pgmqL.css +0 -1
  167. package/dist/web/assets/index-B-wbyZq7.js +0 -386
  168. package/dist/web/assets/index-B1BnOpiO.css +0 -1
  169. package/dist/web/assets/index-B3CnV08_.js +0 -365
  170. package/dist/web/assets/index-B5QjuFh9.css +0 -1
  171. package/dist/web/assets/index-B8FW2ecQ.js +0 -370
  172. package/dist/web/assets/index-BFxPiKyy.js +0 -381
  173. package/dist/web/assets/index-BGqNh_Da.js +0 -365
  174. package/dist/web/assets/index-BIONU0qz.css +0 -1
  175. package/dist/web/assets/index-B_l_3wnA.js +0 -370
  176. package/dist/web/assets/index-Ba-4YvTQ.js +0 -365
  177. package/dist/web/assets/index-Bbb4-39N.js +0 -370
  178. package/dist/web/assets/index-BdgdShEN.js +0 -365
  179. package/dist/web/assets/index-BhiC4Z-G.js +0 -381
  180. package/dist/web/assets/index-BjhCFjZi.js +0 -381
  181. package/dist/web/assets/index-BlRxmrnQ.css +0 -1
  182. package/dist/web/assets/index-BqwbXsHS.js +0 -365
  183. package/dist/web/assets/index-BsC7RT48.css +0 -1
  184. package/dist/web/assets/index-Bvh8XI8_.js +0 -370
  185. package/dist/web/assets/index-C01r2ymn.js +0 -381
  186. package/dist/web/assets/index-C51Rbsmk.js +0 -381
  187. package/dist/web/assets/index-C7lMNxRE.js +0 -370
  188. package/dist/web/assets/index-CJLObgsn.js +0 -386
  189. package/dist/web/assets/index-CLuRf7Zt.js +0 -381
  190. package/dist/web/assets/index-CM3xFd3e.css +0 -1
  191. package/dist/web/assets/index-CNkBtDK7.js +0 -370
  192. package/dist/web/assets/index-CPQbZ1BL.css +0 -1
  193. package/dist/web/assets/index-CTy5dUDU.css +0 -1
  194. package/dist/web/assets/index-CU15COKs.js +0 -370
  195. package/dist/web/assets/index-CiRJZI4z.js +0 -386
  196. package/dist/web/assets/index-Cp7RYjoy.css +0 -1
  197. package/dist/web/assets/index-CvPkZOWT.js +0 -381
  198. package/dist/web/assets/index-CvUk-33B.css +0 -1
  199. package/dist/web/assets/index-Cvmj-oF6.css +0 -1
  200. package/dist/web/assets/index-CxE5iQVO.js +0 -381
  201. package/dist/web/assets/index-D7lZQpFV.js +0 -365
  202. package/dist/web/assets/index-DAyP4GOi.css +0 -1
  203. package/dist/web/assets/index-DCG0n8Kg.js +0 -386
  204. package/dist/web/assets/index-DEEI8cyF.css +0 -1
  205. package/dist/web/assets/index-DF99BowV.js +0 -381
  206. package/dist/web/assets/index-DGWsvFj6.css +0 -1
  207. package/dist/web/assets/index-DGpU39Cp.css +0 -1
  208. package/dist/web/assets/index-DI4kFgOi.js +0 -370
  209. package/dist/web/assets/index-DIyTcPw5.css +0 -1
  210. package/dist/web/assets/index-DTsfDC7m.js +0 -381
  211. package/dist/web/assets/index-DcgNAi4A.js +0 -386
  212. package/dist/web/assets/index-DgOAgkZy.css +0 -1
  213. package/dist/web/assets/index-DnHkqbOa.js +0 -386
  214. package/dist/web/assets/index-DnR7Idcf.css +0 -1
  215. package/dist/web/assets/index-DpVirMEe.css +0 -1
  216. package/dist/web/assets/index-DsYIWZ86.css +0 -1
  217. package/dist/web/assets/index-DtrDkci5.js +0 -381
  218. package/dist/web/assets/index-DyjIth5M.js +0 -386
  219. package/dist/web/assets/index-FwIKfHPL.css +0 -1
  220. package/dist/web/assets/index-IKrZymWk.css +0 -1
  221. package/dist/web/assets/index-MA6fM0ab.js +0 -381
  222. package/dist/web/assets/index-N_QxaSEg.css +0 -1
  223. package/dist/web/assets/index-OcU-0TCQ.css +0 -1
  224. package/dist/web/assets/index-OyhrmG-L.js +0 -381
  225. package/dist/web/assets/index-SQlBKu_s.js +0 -386
  226. package/dist/web/assets/index-Sv2B60J4.js +0 -370
  227. package/dist/web/assets/index-Vgyivb5u.js +0 -365
  228. package/dist/web/assets/index-ZABWMEZR.js +0 -381
  229. package/dist/web/assets/index-ZE5lOeJp.js +0 -386
  230. package/dist/web/assets/index-aCHmkAcJ.css +0 -1
  231. package/dist/web/assets/index-bFijnpuU.js +0 -381
  232. package/dist/web/assets/index-bUPZgSoZ.css +0 -1
  233. package/dist/web/assets/index-mz1VIYsP.css +0 -1
  234. package/dist/web/assets/index-oOp_MFeE.js +0 -376
  235. package/dist/web/assets/index-rfU713Zm.js +0 -386
  236. package/dist/web/assets/index-tuNH9gmb.js +0 -448
  237. package/dist/web/assets/index-viSwHyDD.js +0 -365
  238. package/dist/web/assets/index-yKEQysks.js +0 -365
@@ -1,873 +0,0 @@
1
- /**
2
- * Config tab for the dashboard TUI
3
- * Allows viewing and editing all configuration fields
4
- */
5
- import blessed from 'blessed';
6
- import { BUILT_IN_PRESETS, BUILT_IN_PRESET_IDS, saveConfig, } from '@night-watch/core';
7
- import { performUninstall } from '../uninstall.js';
8
- import { performInstall } from '../install.js';
9
- const SENSITIVE_PATTERNS = /TOKEN|KEY|SECRET|PASSWORD/i;
10
- function promptTextbox(screen, label, initialValue, cb) {
11
- const input = blessed.textbox({
12
- top: 'center',
13
- left: 'center',
14
- width: '50%',
15
- height: 3,
16
- border: { type: 'line' },
17
- label: `[ ${label} ]`,
18
- tags: true,
19
- style: { border: { fg: 'yellow' }, fg: 'white' },
20
- inputOnFocus: true,
21
- });
22
- screen.append(input);
23
- input.setValue(initialValue);
24
- input.focus();
25
- screen.render();
26
- input.on('submit', (value) => {
27
- input.destroy();
28
- cb(value.trim());
29
- });
30
- input.on('cancel', () => {
31
- input.destroy();
32
- cb(null);
33
- });
34
- }
35
- const WEBHOOK_TYPES = ['slack', 'discord', 'telegram'];
36
- const NOTIFICATION_EVENTS = [
37
- 'run_started',
38
- 'run_succeeded',
39
- 'run_failed',
40
- 'run_timeout',
41
- 'review_completed',
42
- 'pr_auto_merged',
43
- 'rate_limit_fallback',
44
- 'qa_completed',
45
- ];
46
- export const CONFIG_FIELDS = [
47
- { key: 'provider', label: 'Provider', type: 'enum', options: [...BUILT_IN_PRESET_IDS] },
48
- { key: 'reviewerEnabled', label: 'Reviewer Enabled', type: 'boolean' },
49
- { key: 'defaultBranch', label: 'Default Branch', type: 'string' },
50
- { key: 'prdDir', label: 'PRD Directory', type: 'string' },
51
- { key: 'branchPrefix', label: 'Branch Prefix', type: 'string' },
52
- { key: 'branchPatterns', label: 'Branch Patterns', type: 'string[]' },
53
- { key: 'cronSchedule', label: 'Executor Schedule', type: 'string' },
54
- { key: 'reviewerSchedule', label: 'Reviewer Schedule', type: 'string' },
55
- {
56
- key: 'maxRuntime',
57
- label: 'Max Runtime (s)',
58
- type: 'number',
59
- validate: (v) => {
60
- const n = parseInt(v, 10);
61
- return isNaN(n) || n <= 0 ? 'Must be a positive integer' : null;
62
- },
63
- },
64
- {
65
- key: 'reviewerMaxRuntime',
66
- label: 'Reviewer Max Runtime (s)',
67
- type: 'number',
68
- validate: (v) => {
69
- const n = parseInt(v, 10);
70
- return isNaN(n) || n <= 0 ? 'Must be a positive integer' : null;
71
- },
72
- },
73
- {
74
- key: 'minReviewScore',
75
- label: 'Min Review Score',
76
- type: 'number',
77
- validate: (v) => {
78
- const n = parseInt(v, 10);
79
- return isNaN(n) || n < 0 || n > 100 ? 'Must be 0-100' : null;
80
- },
81
- },
82
- {
83
- key: 'maxLogSize',
84
- label: 'Max Log Size (bytes)',
85
- type: 'number',
86
- validate: (v) => {
87
- const n = parseInt(v, 10);
88
- return isNaN(n) || n <= 0 ? 'Must be a positive integer' : null;
89
- },
90
- },
91
- { key: 'providerEnv', label: 'Provider Env Vars', type: 'keyvalue' },
92
- { key: 'notifications', label: 'Notifications', type: 'webhooks' },
93
- ];
94
- function maskValue(key, value) {
95
- if (SENSITIVE_PATTERNS.test(key) && value.length > 6) {
96
- return value.slice(0, 3) + '***' + value.slice(-3);
97
- }
98
- return value;
99
- }
100
- function formatFieldValue(config, field) {
101
- const value = config[field.key];
102
- if (field.type === 'string[]' && Array.isArray(value)) {
103
- return value.join(', ');
104
- }
105
- if (field.type === 'keyvalue') {
106
- const env = value;
107
- const count = Object.keys(env).length;
108
- return count > 0 ? `${count} variable(s) set` : '(none)';
109
- }
110
- if (field.type === 'webhooks') {
111
- const notif = value;
112
- return notif.webhooks.length > 0 ? `${notif.webhooks.length} webhook(s)` : '(none)';
113
- }
114
- return String(value);
115
- }
116
- /**
117
- * Create the Config editor tab
118
- */
119
- export function createConfigTab() {
120
- const container = blessed.box({
121
- top: 0,
122
- left: 0,
123
- width: '100%',
124
- height: '100%',
125
- hidden: true,
126
- });
127
- const configList = blessed.list({
128
- top: 0,
129
- left: 0,
130
- width: '100%',
131
- height: '100%-3',
132
- border: { type: 'line' },
133
- label: '[ Configuration ]',
134
- tags: true,
135
- scrollable: true,
136
- alwaysScroll: true,
137
- scrollbar: { style: { bg: 'blue' } },
138
- style: {
139
- border: { fg: 'cyan' },
140
- selected: { bg: 'blue', fg: 'white' },
141
- item: { fg: 'white' },
142
- },
143
- keys: true,
144
- vi: false,
145
- mouse: false,
146
- interactive: true,
147
- });
148
- const statusBar = blessed.box({
149
- bottom: 0,
150
- left: 0,
151
- width: '100%',
152
- height: 3,
153
- border: { type: 'line' },
154
- tags: true,
155
- style: { border: { fg: 'white' } },
156
- content: '',
157
- });
158
- container.append(configList);
159
- container.append(statusBar);
160
- const pendingChanges = {};
161
- let currentConfig = null;
162
- function buildListItems(config) {
163
- return CONFIG_FIELDS.map((field) => {
164
- const hasChange = field.key in pendingChanges;
165
- const value = hasChange
166
- ? formatFieldValue({ ...config, ...pendingChanges }, field)
167
- : formatFieldValue(config, field);
168
- const marker = hasChange ? ' {yellow-fg}*{/yellow-fg}' : '';
169
- const editHint = field.type === 'keyvalue' || field.type === 'webhooks'
170
- ? ' {#888888-fg}(Enter to manage){/#888888-fg}'
171
- : '';
172
- return ` ${field.label}: ${value}${marker}${editHint}`;
173
- });
174
- }
175
- function updateStatusBar() {
176
- const changeCount = Object.keys(pendingChanges).length;
177
- if (changeCount > 0) {
178
- statusBar.setContent(` {yellow-fg}${changeCount} unsaved change(s){/yellow-fg} | s:Save & Apply u:Undo All`);
179
- }
180
- else {
181
- statusBar.setContent(' No pending changes');
182
- }
183
- }
184
- function refreshList(config) {
185
- configList.setItems(buildListItems(config));
186
- updateStatusBar();
187
- }
188
- // ── Key-Value Editor (providerEnv) ──────────────────────────────────────
189
- function showKeyValueEditor(ctx, config) {
190
- const currentEnv = {
191
- ...(config.providerEnv || {}),
192
- ...(pendingChanges.providerEnv || {}),
193
- };
194
- // If pendingChanges has providerEnv, use it entirely; otherwise merge from config
195
- const editableEnv = pendingChanges.providerEnv
196
- ? { ...pendingChanges.providerEnv }
197
- : { ...currentEnv };
198
- function buildItems() {
199
- const entries = Object.entries(editableEnv);
200
- if (entries.length === 0)
201
- return [' (no variables set)'];
202
- return entries.map(([k, v]) => ` ${k} = ${maskValue(k, v)}`);
203
- }
204
- const kvList = blessed.list({
205
- top: 'center',
206
- left: 'center',
207
- width: '70%',
208
- height: Math.min(Object.keys(editableEnv).length + 4, 20),
209
- border: { type: 'line' },
210
- label: '[ Provider Env Vars | a:Add Enter:Edit d:Delete Esc:Done ]',
211
- tags: true,
212
- style: {
213
- border: { fg: 'cyan' },
214
- selected: { bg: 'blue', fg: 'white' },
215
- item: { fg: 'white' },
216
- },
217
- keys: true,
218
- vi: false,
219
- interactive: true,
220
- });
221
- kvList.setItems(buildItems());
222
- ctx.setEditing(true);
223
- ctx.screen.append(kvList);
224
- kvList.focus();
225
- ctx.screen.render();
226
- function refreshKvList() {
227
- kvList.setItems(buildItems());
228
- kvList.height = Math.min(Object.keys(editableEnv).length + 4, 20);
229
- ctx.screen.render();
230
- }
231
- kvList.key(['a'], () => {
232
- promptTextbox(ctx.screen, 'Variable Name', '', (key) => {
233
- if (!key) {
234
- kvList.focus();
235
- ctx.screen.render();
236
- return;
237
- }
238
- // eslint-disable-next-line sonarjs/no-nested-functions
239
- promptTextbox(ctx.screen, `Value for ${key}`, '', (value) => {
240
- if (value !== null) {
241
- editableEnv[key] = value;
242
- pendingChanges.providerEnv = { ...editableEnv };
243
- refreshKvList();
244
- }
245
- kvList.focus();
246
- ctx.screen.render();
247
- });
248
- });
249
- });
250
- kvList.key(['enter'], () => {
251
- const keys = Object.keys(editableEnv);
252
- if (keys.length === 0)
253
- return;
254
- const idx = kvList.selected;
255
- if (idx < 0 || idx >= keys.length)
256
- return;
257
- const selectedKey = keys[idx];
258
- promptTextbox(ctx.screen, `Edit ${selectedKey}`, editableEnv[selectedKey], (value) => {
259
- if (value !== null) {
260
- editableEnv[selectedKey] = value;
261
- pendingChanges.providerEnv = { ...editableEnv };
262
- refreshKvList();
263
- }
264
- kvList.focus();
265
- ctx.screen.render();
266
- });
267
- });
268
- kvList.key(['d'], () => {
269
- const keys = Object.keys(editableEnv);
270
- if (keys.length === 0)
271
- return;
272
- const idx = kvList.selected;
273
- if (idx < 0 || idx >= keys.length)
274
- return;
275
- const selectedKey = keys[idx];
276
- delete editableEnv[selectedKey];
277
- pendingChanges.providerEnv = { ...editableEnv };
278
- refreshKvList();
279
- });
280
- kvList.key(['escape'], () => {
281
- kvList.destroy();
282
- ctx.setEditing(false);
283
- if (currentConfig)
284
- refreshList(currentConfig);
285
- configList.focus();
286
- ctx.screen.render();
287
- });
288
- }
289
- // ── Webhook Editor (notifications) ──────────────────────────────────────
290
- function showWebhookEditor(ctx, config) {
291
- const currentNotif = pendingChanges.notifications
292
- ? pendingChanges.notifications
293
- : config.notifications;
294
- const editableWebhooks = currentNotif.webhooks.map((w) => ({
295
- ...w,
296
- events: [...w.events],
297
- }));
298
- function buildItems() {
299
- if (editableWebhooks.length === 0)
300
- return [' (no webhooks configured)'];
301
- return editableWebhooks.map((w) => {
302
- let identifier;
303
- if (w.type === 'telegram') {
304
- identifier = `token:${maskValue('TOKEN', w.botToken || '')}`;
305
- }
306
- else {
307
- identifier = w.url ? maskValue('URL', w.url) : 'no url';
308
- }
309
- return ` [${w.type}] ${identifier} events: ${w.events.length}`;
310
- });
311
- }
312
- const whList = blessed.list({
313
- top: 'center',
314
- left: 'center',
315
- width: '70%',
316
- height: Math.min(editableWebhooks.length + 4, 20),
317
- border: { type: 'line' },
318
- label: '[ Webhooks | a:Add Enter:Edit d:Delete Esc:Done ]',
319
- tags: true,
320
- style: {
321
- border: { fg: 'cyan' },
322
- selected: { bg: 'blue', fg: 'white' },
323
- item: { fg: 'white' },
324
- },
325
- keys: true,
326
- vi: false,
327
- interactive: true,
328
- });
329
- whList.setItems(buildItems());
330
- ctx.setEditing(true);
331
- ctx.screen.append(whList);
332
- whList.focus();
333
- ctx.screen.render();
334
- function refreshWhList() {
335
- whList.setItems(buildItems());
336
- whList.height = Math.min(editableWebhooks.length + 4, 20);
337
- ctx.screen.render();
338
- }
339
- function stageWebhookChanges() {
340
- pendingChanges.notifications = {
341
- webhooks: editableWebhooks.map((w) => ({ ...w, events: [...w.events] })),
342
- };
343
- }
344
- function selectType(cb) {
345
- const typeList = blessed.list({
346
- top: 'center',
347
- left: 'center',
348
- width: 30,
349
- height: WEBHOOK_TYPES.length + 2,
350
- border: { type: 'line' },
351
- label: '[ Webhook Type ]',
352
- tags: true,
353
- style: {
354
- border: { fg: 'yellow' },
355
- selected: { bg: 'blue', fg: 'white' },
356
- item: { fg: 'white' },
357
- },
358
- keys: true,
359
- vi: false,
360
- interactive: true,
361
- });
362
- typeList.setItems(WEBHOOK_TYPES);
363
- ctx.screen.append(typeList);
364
- typeList.focus();
365
- ctx.screen.render();
366
- typeList.on('select', (_item, index) => {
367
- typeList.destroy();
368
- cb(WEBHOOK_TYPES[index]);
369
- });
370
- typeList.key(['escape'], () => {
371
- typeList.destroy();
372
- cb(null);
373
- });
374
- }
375
- function selectEvents(current, cb) {
376
- const selected = new Set(current);
377
- const evList = blessed.list({
378
- top: 'center',
379
- left: 'center',
380
- width: 40,
381
- height: NOTIFICATION_EVENTS.length + 3,
382
- border: { type: 'line' },
383
- label: '[ Events | Space:Toggle Enter:Done ]',
384
- tags: true,
385
- style: {
386
- border: { fg: 'yellow' },
387
- selected: { bg: 'blue', fg: 'white' },
388
- item: { fg: 'white' },
389
- },
390
- keys: true,
391
- vi: false,
392
- interactive: true,
393
- });
394
- function renderEvents() {
395
- evList.setItems(NOTIFICATION_EVENTS.map(
396
- // eslint-disable-next-line sonarjs/no-nested-functions
397
- (e) => ` ${selected.has(e) ? '[x]' : '[ ]'} ${e}`));
398
- }
399
- renderEvents();
400
- ctx.screen.append(evList);
401
- evList.focus();
402
- ctx.screen.render();
403
- evList.key(['space'], () => {
404
- const idx = evList.selected;
405
- if (idx >= 0 && idx < NOTIFICATION_EVENTS.length) {
406
- const ev = NOTIFICATION_EVENTS[idx];
407
- if (selected.has(ev))
408
- selected.delete(ev);
409
- else
410
- selected.add(ev);
411
- renderEvents();
412
- evList.select(idx);
413
- ctx.screen.render();
414
- }
415
- });
416
- evList.key(['enter'], () => {
417
- evList.destroy();
418
- cb([...selected]);
419
- });
420
- evList.key(['escape'], () => {
421
- evList.destroy();
422
- cb(null);
423
- });
424
- }
425
- function addWebhookWizard() {
426
- selectType((type) => {
427
- if (!type) {
428
- whList.focus();
429
- ctx.screen.render();
430
- return;
431
- }
432
- const webhook = { type, events: [...NOTIFICATION_EVENTS] };
433
- // eslint-disable-next-line sonarjs/no-nested-functions
434
- const askCredentials = (done) => {
435
- if (type === 'telegram') {
436
- promptTextbox(ctx.screen, 'Bot Token', '', (botToken) => {
437
- if (botToken === null) {
438
- done(false);
439
- return;
440
- }
441
- webhook.botToken = botToken;
442
- promptTextbox(ctx.screen, 'Chat ID', '', (chatId) => {
443
- if (chatId === null) {
444
- done(false);
445
- return;
446
- }
447
- webhook.chatId = chatId;
448
- done(true);
449
- });
450
- });
451
- }
452
- else {
453
- promptTextbox(ctx.screen, 'Webhook URL', '', (url) => {
454
- if (url === null) {
455
- done(false);
456
- return;
457
- }
458
- webhook.url = url;
459
- done(true);
460
- });
461
- }
462
- };
463
- // eslint-disable-next-line sonarjs/no-nested-functions
464
- askCredentials((ok) => {
465
- if (!ok) {
466
- whList.focus();
467
- ctx.screen.render();
468
- return;
469
- }
470
- selectEvents(webhook.events, (events) => {
471
- if (events === null) {
472
- whList.focus();
473
- ctx.screen.render();
474
- return;
475
- }
476
- webhook.events = events;
477
- editableWebhooks.push(webhook);
478
- stageWebhookChanges();
479
- refreshWhList();
480
- whList.focus();
481
- ctx.screen.render();
482
- });
483
- });
484
- });
485
- }
486
- function editWebhook(idx) {
487
- const webhook = editableWebhooks[idx];
488
- selectType((type) => {
489
- if (type === null) {
490
- whList.focus();
491
- ctx.screen.render();
492
- return;
493
- }
494
- webhook.type = type;
495
- // eslint-disable-next-line sonarjs/no-nested-functions
496
- const askCredentials = (done) => {
497
- if (type === 'telegram') {
498
- promptTextbox(ctx.screen, 'Bot Token', webhook.botToken || '', (botToken) => {
499
- if (botToken === null) {
500
- done(false);
501
- return;
502
- }
503
- webhook.botToken = botToken;
504
- webhook.url = undefined;
505
- promptTextbox(ctx.screen, 'Chat ID', webhook.chatId || '', (chatId) => {
506
- if (chatId === null) {
507
- done(false);
508
- return;
509
- }
510
- webhook.chatId = chatId;
511
- done(true);
512
- });
513
- });
514
- }
515
- else {
516
- promptTextbox(ctx.screen, 'Webhook URL', webhook.url || '', (url) => {
517
- if (url === null) {
518
- done(false);
519
- return;
520
- }
521
- webhook.url = url;
522
- webhook.botToken = undefined;
523
- webhook.chatId = undefined;
524
- done(true);
525
- });
526
- }
527
- };
528
- // eslint-disable-next-line sonarjs/no-nested-functions
529
- askCredentials((ok) => {
530
- if (!ok) {
531
- whList.focus();
532
- ctx.screen.render();
533
- return;
534
- }
535
- selectEvents(webhook.events, (events) => {
536
- if (events === null) {
537
- whList.focus();
538
- ctx.screen.render();
539
- return;
540
- }
541
- webhook.events = events;
542
- stageWebhookChanges();
543
- refreshWhList();
544
- whList.focus();
545
- ctx.screen.render();
546
- });
547
- });
548
- });
549
- }
550
- whList.key(['a'], () => addWebhookWizard());
551
- whList.key(['enter'], () => {
552
- if (editableWebhooks.length === 0)
553
- return;
554
- const idx = whList.selected;
555
- if (idx >= 0 && idx < editableWebhooks.length) {
556
- editWebhook(idx);
557
- }
558
- });
559
- whList.key(['d'], () => {
560
- if (editableWebhooks.length === 0)
561
- return;
562
- const idx = whList.selected;
563
- if (idx >= 0 && idx < editableWebhooks.length) {
564
- editableWebhooks.splice(idx, 1);
565
- stageWebhookChanges();
566
- refreshWhList();
567
- }
568
- });
569
- whList.key(['escape'], () => {
570
- whList.destroy();
571
- ctx.setEditing(false);
572
- if (currentConfig)
573
- refreshList(currentConfig);
574
- configList.focus();
575
- ctx.screen.render();
576
- });
577
- }
578
- // ── GLM-5 Quick Setup ──────────────────────────────────────────────────
579
- function showGlm5Setup(ctx) {
580
- const inputBox = blessed.textbox({
581
- top: 'center',
582
- left: 'center',
583
- width: '60%',
584
- height: 3,
585
- border: { type: 'line' },
586
- label: '[ GLM-5 Quick Setup: Enter API Key ]',
587
- tags: true,
588
- style: { border: { fg: 'cyan' }, fg: 'white' },
589
- inputOnFocus: true,
590
- });
591
- ctx.setEditing(true);
592
- ctx.screen.append(inputBox);
593
- inputBox.setValue('');
594
- inputBox.focus();
595
- ctx.screen.render();
596
- inputBox.on('submit', (value) => {
597
- const apiKey = value.trim();
598
- inputBox.destroy();
599
- ctx.setEditing(false);
600
- if (!apiKey) {
601
- ctx.showMessage('No API key provided', 'error');
602
- configList.focus();
603
- ctx.screen.render();
604
- return;
605
- }
606
- pendingChanges.provider = 'glm-5';
607
- pendingChanges.providerPresets = {
608
- 'glm-5': {
609
- ...BUILT_IN_PRESETS['glm-5'],
610
- envVars: {
611
- ...BUILT_IN_PRESETS['glm-5'].envVars,
612
- ANTHROPIC_API_KEY: apiKey,
613
- ANTHROPIC_AUTH_TOKEN: apiKey,
614
- },
615
- },
616
- };
617
- ctx.showMessage('GLM-5 configured. Press s to save.', 'success');
618
- if (currentConfig)
619
- refreshList(currentConfig);
620
- configList.focus();
621
- ctx.screen.render();
622
- });
623
- inputBox.on('cancel', () => {
624
- inputBox.destroy();
625
- ctx.setEditing(false);
626
- configList.focus();
627
- ctx.screen.render();
628
- });
629
- }
630
- // ── Standard Editor ─────────────────────────────────────────────────────
631
- function showEditor(ctx, field, config) {
632
- if (field.type === 'keyvalue') {
633
- showKeyValueEditor(ctx, config);
634
- return;
635
- }
636
- if (field.type === 'webhooks') {
637
- showWebhookEditor(ctx, config);
638
- return;
639
- }
640
- const currentValue = field.key in pendingChanges
641
- ? String(pendingChanges[field.key])
642
- : formatFieldValue(config, field);
643
- if (field.type === 'enum' || field.type === 'boolean') {
644
- const options = field.type === 'boolean' ? ['true', 'false'] : field.options || [];
645
- const selectorList = blessed.list({
646
- top: 'center',
647
- left: 'center',
648
- width: Math.max(30, ...options.map((o) => o.length + 6)),
649
- height: options.length + 2,
650
- border: { type: 'line' },
651
- label: `[ ${field.label} ]`,
652
- tags: true,
653
- style: {
654
- border: { fg: 'cyan' },
655
- selected: { bg: 'blue', fg: 'white' },
656
- item: { fg: 'white' },
657
- },
658
- keys: true,
659
- vi: false,
660
- interactive: true,
661
- });
662
- selectorList.setItems(options);
663
- // Pre-select current value
664
- const currentIdx = options.indexOf(currentValue);
665
- if (currentIdx >= 0) {
666
- selectorList.select(currentIdx);
667
- }
668
- ctx.setEditing(true);
669
- ctx.screen.append(selectorList);
670
- selectorList.focus();
671
- ctx.screen.render();
672
- selectorList.on('select', (_item, index) => {
673
- const selected = options[index];
674
- if (field.type === 'boolean') {
675
- pendingChanges[field.key] = selected === 'true';
676
- }
677
- else {
678
- pendingChanges[field.key] = selected;
679
- }
680
- selectorList.destroy();
681
- ctx.setEditing(false);
682
- refreshList(config);
683
- configList.focus();
684
- ctx.screen.render();
685
- });
686
- selectorList.key(['escape'], () => {
687
- selectorList.destroy();
688
- ctx.setEditing(false);
689
- configList.focus();
690
- ctx.screen.render();
691
- });
692
- return;
693
- }
694
- // Text input for string, number, string[]
695
- const inputBox = blessed.textbox({
696
- top: 'center',
697
- left: 'center',
698
- width: '60%',
699
- height: 3,
700
- border: { type: 'line' },
701
- label: `[ ${field.label} ]`,
702
- tags: true,
703
- style: {
704
- border: { fg: 'cyan' },
705
- fg: 'white',
706
- },
707
- inputOnFocus: true,
708
- });
709
- ctx.setEditing(true);
710
- ctx.screen.append(inputBox);
711
- inputBox.setValue(currentValue);
712
- inputBox.focus();
713
- ctx.screen.render();
714
- inputBox.on('submit', (value) => {
715
- // Validate
716
- if (field.validate) {
717
- const error = field.validate(value);
718
- if (error) {
719
- ctx.showMessage(error, 'error');
720
- inputBox.destroy();
721
- ctx.setEditing(false);
722
- configList.focus();
723
- ctx.screen.render();
724
- return;
725
- }
726
- }
727
- // Apply value
728
- if (field.type === 'number') {
729
- pendingChanges[field.key] = parseInt(value, 10);
730
- }
731
- else if (field.type === 'string[]') {
732
- pendingChanges[field.key] = value
733
- .split(',')
734
- .map((s) => s.trim())
735
- .filter((s) => s.length > 0);
736
- }
737
- else {
738
- pendingChanges[field.key] = value;
739
- }
740
- inputBox.destroy();
741
- ctx.setEditing(false);
742
- refreshList(config);
743
- configList.focus();
744
- ctx.screen.render();
745
- });
746
- inputBox.on('cancel', () => {
747
- inputBox.destroy();
748
- ctx.setEditing(false);
749
- configList.focus();
750
- ctx.screen.render();
751
- });
752
- }
753
- let activeKeyHandlers = [];
754
- let activeCtx = null;
755
- function bindKeys(ctx) {
756
- const handlers = [
757
- [
758
- ['enter'],
759
- () => {
760
- const idx = configList.selected;
761
- if (idx === undefined || idx < 0 || idx >= CONFIG_FIELDS.length)
762
- return;
763
- const field = CONFIG_FIELDS[idx];
764
- if (currentConfig) {
765
- showEditor(ctx, field, currentConfig);
766
- }
767
- },
768
- ],
769
- [
770
- ['s'],
771
- () => {
772
- if (Object.keys(pendingChanges).length === 0) {
773
- ctx.showMessage('No changes to save', 'info');
774
- return;
775
- }
776
- // Save config
777
- const result = saveConfig(ctx.projectDir, pendingChanges);
778
- if (!result.success) {
779
- ctx.showMessage(`Save failed: ${result.error}`, 'error');
780
- return;
781
- }
782
- // Check if schedules changed - reinstall cron
783
- const scheduleChanged = 'cronSchedule' in pendingChanges ||
784
- 'reviewerSchedule' in pendingChanges ||
785
- 'reviewerEnabled' in pendingChanges;
786
- if (scheduleChanged) {
787
- performUninstall(ctx.projectDir, { keepLogs: true });
788
- const newConfig = ctx.reloadConfig();
789
- const installResult = performInstall(ctx.projectDir, newConfig);
790
- if (!installResult.success) {
791
- ctx.showMessage(`Config saved but cron reinstall failed: ${installResult.error}`, 'error');
792
- }
793
- else {
794
- ctx.showMessage('Config saved & cron reinstalled', 'success');
795
- }
796
- }
797
- else {
798
- ctx.showMessage('Config saved', 'success');
799
- }
800
- // Reload config
801
- currentConfig = ctx.reloadConfig();
802
- // Clear pending
803
- for (const key of Object.keys(pendingChanges)) {
804
- delete pendingChanges[key];
805
- }
806
- refreshList(currentConfig);
807
- ctx.screen.render();
808
- },
809
- ],
810
- [
811
- ['u'],
812
- () => {
813
- for (const key of Object.keys(pendingChanges)) {
814
- delete pendingChanges[key];
815
- }
816
- if (currentConfig) {
817
- refreshList(currentConfig);
818
- }
819
- ctx.showMessage('Changes undone', 'info');
820
- ctx.screen.render();
821
- },
822
- ],
823
- [
824
- ['g'],
825
- () => {
826
- showGlm5Setup(ctx);
827
- },
828
- ],
829
- ];
830
- for (const [keys, handler] of handlers) {
831
- ctx.screen.key(keys, handler);
832
- }
833
- activeKeyHandlers = handlers;
834
- }
835
- function unbindKeys(ctx) {
836
- for (const [keys, handler] of activeKeyHandlers) {
837
- for (const key of keys) {
838
- ctx.screen.unkey(key, handler);
839
- }
840
- }
841
- activeKeyHandlers = [];
842
- }
843
- return {
844
- name: 'Config',
845
- container,
846
- activate(ctx) {
847
- ctx.setFooter(' \u2191\u2193:Navigate Enter:Edit g:GLM-5 Setup s:Save u:Undo q:Quit');
848
- currentConfig = ctx.config;
849
- refreshList(currentConfig);
850
- configList.focus();
851
- activeCtx = ctx;
852
- bindKeys(ctx);
853
- ctx.screen.render();
854
- },
855
- deactivate() {
856
- if (activeCtx) {
857
- unbindKeys(activeCtx);
858
- activeCtx = null;
859
- }
860
- },
861
- refresh(ctx) {
862
- // Only refresh if no pending changes (don't overwrite user edits)
863
- if (Object.keys(pendingChanges).length === 0) {
864
- currentConfig = ctx.config;
865
- refreshList(currentConfig);
866
- }
867
- },
868
- destroy() {
869
- // Nothing to clean up
870
- },
871
- };
872
- }
873
- //# sourceMappingURL=tab-config.js.map