@jonit-dev/night-watch-cli 1.7.18 → 1.7.19

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