@xyteai/cli 0.1.0

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 (251) hide show
  1. package/LICENSE +176 -0
  2. package/README.md +245 -0
  3. package/dist/bin/xyte-cli.d.ts +3 -0
  4. package/dist/bin/xyte-cli.d.ts.map +1 -0
  5. package/dist/bin/xyte-cli.js +18 -0
  6. package/dist/bin/xyte-cli.js.map +1 -0
  7. package/dist/cli/index.d.ts +24 -0
  8. package/dist/cli/index.d.ts.map +1 -0
  9. package/dist/cli/index.js +1185 -0
  10. package/dist/cli/index.js.map +1 -0
  11. package/dist/client/catalog.d.ts +6 -0
  12. package/dist/client/catalog.d.ts.map +1 -0
  13. package/dist/client/catalog.js +32 -0
  14. package/dist/client/catalog.js.map +1 -0
  15. package/dist/client/create-client.d.ts +3 -0
  16. package/dist/client/create-client.d.ts.map +1 -0
  17. package/dist/client/create-client.js +235 -0
  18. package/dist/client/create-client.js.map +1 -0
  19. package/dist/config/connectivity.d.ts +19 -0
  20. package/dist/config/connectivity.d.ts.map +1 -0
  21. package/dist/config/connectivity.js +166 -0
  22. package/dist/config/connectivity.js.map +1 -0
  23. package/dist/config/readiness.d.ts +32 -0
  24. package/dist/config/readiness.d.ts.map +1 -0
  25. package/dist/config/readiness.js +96 -0
  26. package/dist/config/readiness.js.map +1 -0
  27. package/dist/config/retry-policy.d.ts +16 -0
  28. package/dist/config/retry-policy.d.ts.map +1 -0
  29. package/dist/config/retry-policy.js +24 -0
  30. package/dist/config/retry-policy.js.map +1 -0
  31. package/dist/contracts/call-envelope.d.ts +74 -0
  32. package/dist/contracts/call-envelope.d.ts.map +1 -0
  33. package/dist/contracts/call-envelope.js +72 -0
  34. package/dist/contracts/call-envelope.js.map +1 -0
  35. package/dist/contracts/problem.d.ts +11 -0
  36. package/dist/contracts/problem.d.ts.map +1 -0
  37. package/dist/contracts/problem.js +55 -0
  38. package/dist/contracts/problem.js.map +1 -0
  39. package/dist/contracts/versions.d.ts +6 -0
  40. package/dist/contracts/versions.d.ts.map +1 -0
  41. package/dist/contracts/versions.js +9 -0
  42. package/dist/contracts/versions.js.map +1 -0
  43. package/dist/http/errors.d.ts +24 -0
  44. package/dist/http/errors.d.ts.map +1 -0
  45. package/dist/http/errors.js +39 -0
  46. package/dist/http/errors.js.map +1 -0
  47. package/dist/http/transport.d.ts +35 -0
  48. package/dist/http/transport.d.ts.map +1 -0
  49. package/dist/http/transport.js +129 -0
  50. package/dist/http/transport.js.map +1 -0
  51. package/dist/index.d.ts +10 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +23 -0
  54. package/dist/index.js.map +1 -0
  55. package/dist/mcp/server.d.ts +12 -0
  56. package/dist/mcp/server.d.ts.map +1 -0
  57. package/dist/mcp/server.js +404 -0
  58. package/dist/mcp/server.js.map +1 -0
  59. package/dist/namespaces/device.d.ts +38 -0
  60. package/dist/namespaces/device.d.ts.map +1 -0
  61. package/dist/namespaces/device.js +36 -0
  62. package/dist/namespaces/device.js.map +1 -0
  63. package/dist/namespaces/organization.d.ts +27 -0
  64. package/dist/namespaces/organization.d.ts.map +1 -0
  65. package/dist/namespaces/organization.js +30 -0
  66. package/dist/namespaces/organization.js.map +1 -0
  67. package/dist/namespaces/partner.d.ts +18 -0
  68. package/dist/namespaces/partner.d.ts.map +1 -0
  69. package/dist/namespaces/partner.js +21 -0
  70. package/dist/namespaces/partner.js.map +1 -0
  71. package/dist/observability/logger.d.ts +3 -0
  72. package/dist/observability/logger.d.ts.map +1 -0
  73. package/dist/observability/logger.js +21 -0
  74. package/dist/observability/logger.js.map +1 -0
  75. package/dist/observability/tracing.d.ts +3 -0
  76. package/dist/observability/tracing.d.ts.map +1 -0
  77. package/dist/observability/tracing.js +26 -0
  78. package/dist/observability/tracing.js.map +1 -0
  79. package/dist/secure/key-slots.d.ts +8 -0
  80. package/dist/secure/key-slots.d.ts.map +1 -0
  81. package/dist/secure/key-slots.js +47 -0
  82. package/dist/secure/key-slots.js.map +1 -0
  83. package/dist/secure/keychain.d.ts +20 -0
  84. package/dist/secure/keychain.d.ts.map +1 -0
  85. package/dist/secure/keychain.js +170 -0
  86. package/dist/secure/keychain.js.map +1 -0
  87. package/dist/secure/profile-store.d.ts +66 -0
  88. package/dist/secure/profile-store.d.ts.map +1 -0
  89. package/dist/secure/profile-store.js +309 -0
  90. package/dist/secure/profile-store.js.map +1 -0
  91. package/dist/spec/public-endpoints.json +1175 -0
  92. package/dist/tui/animation.d.ts +12 -0
  93. package/dist/tui/animation.d.ts.map +1 -0
  94. package/dist/tui/animation.js +41 -0
  95. package/dist/tui/animation.js.map +1 -0
  96. package/dist/tui/app.d.ts +27 -0
  97. package/dist/tui/app.d.ts.map +1 -0
  98. package/dist/tui/app.js +711 -0
  99. package/dist/tui/app.js.map +1 -0
  100. package/dist/tui/assets/logo.d.ts +5 -0
  101. package/dist/tui/assets/logo.d.ts.map +1 -0
  102. package/dist/tui/assets/logo.js +24 -0
  103. package/dist/tui/assets/logo.js.map +1 -0
  104. package/dist/tui/data-loaders.d.ts +33 -0
  105. package/dist/tui/data-loaders.d.ts.map +1 -0
  106. package/dist/tui/data-loaders.js +250 -0
  107. package/dist/tui/data-loaders.js.map +1 -0
  108. package/dist/tui/dispatch.d.ts +14 -0
  109. package/dist/tui/dispatch.d.ts.map +1 -0
  110. package/dist/tui/dispatch.js +44 -0
  111. package/dist/tui/dispatch.js.map +1 -0
  112. package/dist/tui/headless-renderer.d.ts +20 -0
  113. package/dist/tui/headless-renderer.d.ts.map +1 -0
  114. package/dist/tui/headless-renderer.js +598 -0
  115. package/dist/tui/headless-renderer.js.map +1 -0
  116. package/dist/tui/input-controller.d.ts +29 -0
  117. package/dist/tui/input-controller.d.ts.map +1 -0
  118. package/dist/tui/input-controller.js +76 -0
  119. package/dist/tui/input-controller.js.map +1 -0
  120. package/dist/tui/key-wizard.d.ts +29 -0
  121. package/dist/tui/key-wizard.d.ts.map +1 -0
  122. package/dist/tui/key-wizard.js +177 -0
  123. package/dist/tui/key-wizard.js.map +1 -0
  124. package/dist/tui/keymap.d.ts +9 -0
  125. package/dist/tui/keymap.d.ts.map +1 -0
  126. package/dist/tui/keymap.js +29 -0
  127. package/dist/tui/keymap.js.map +1 -0
  128. package/dist/tui/layout.d.ts +16 -0
  129. package/dist/tui/layout.d.ts.map +1 -0
  130. package/dist/tui/layout.js +99 -0
  131. package/dist/tui/layout.js.map +1 -0
  132. package/dist/tui/logger.d.ts +12 -0
  133. package/dist/tui/logger.d.ts.map +1 -0
  134. package/dist/tui/logger.js +83 -0
  135. package/dist/tui/logger.js.map +1 -0
  136. package/dist/tui/navigation.d.ts +26 -0
  137. package/dist/tui/navigation.d.ts.map +1 -0
  138. package/dist/tui/navigation.js +136 -0
  139. package/dist/tui/navigation.js.map +1 -0
  140. package/dist/tui/panes.d.ts +7 -0
  141. package/dist/tui/panes.d.ts.map +1 -0
  142. package/dist/tui/panes.js +34 -0
  143. package/dist/tui/panes.js.map +1 -0
  144. package/dist/tui/runtime.d.ts +34 -0
  145. package/dist/tui/runtime.d.ts.map +1 -0
  146. package/dist/tui/runtime.js +100 -0
  147. package/dist/tui/runtime.js.map +1 -0
  148. package/dist/tui/scene.d.ts +160 -0
  149. package/dist/tui/scene.d.ts.map +1 -0
  150. package/dist/tui/scene.js +424 -0
  151. package/dist/tui/scene.js.map +1 -0
  152. package/dist/tui/screens/config.d.ts +3 -0
  153. package/dist/tui/screens/config.d.ts.map +1 -0
  154. package/dist/tui/screens/config.js +406 -0
  155. package/dist/tui/screens/config.js.map +1 -0
  156. package/dist/tui/screens/dashboard.d.ts +3 -0
  157. package/dist/tui/screens/dashboard.d.ts.map +1 -0
  158. package/dist/tui/screens/dashboard.js +176 -0
  159. package/dist/tui/screens/dashboard.js.map +1 -0
  160. package/dist/tui/screens/devices.d.ts +3 -0
  161. package/dist/tui/screens/devices.d.ts.map +1 -0
  162. package/dist/tui/screens/devices.js +297 -0
  163. package/dist/tui/screens/devices.js.map +1 -0
  164. package/dist/tui/screens/incidents.d.ts +4 -0
  165. package/dist/tui/screens/incidents.d.ts.map +1 -0
  166. package/dist/tui/screens/incidents.js +304 -0
  167. package/dist/tui/screens/incidents.js.map +1 -0
  168. package/dist/tui/screens/setup.d.ts +3 -0
  169. package/dist/tui/screens/setup.d.ts.map +1 -0
  170. package/dist/tui/screens/setup.js +299 -0
  171. package/dist/tui/screens/setup.js.map +1 -0
  172. package/dist/tui/screens/spaces.d.ts +7 -0
  173. package/dist/tui/screens/spaces.d.ts.map +1 -0
  174. package/dist/tui/screens/spaces.js +422 -0
  175. package/dist/tui/screens/spaces.js.map +1 -0
  176. package/dist/tui/screens/tickets.d.ts +9 -0
  177. package/dist/tui/screens/tickets.d.ts.map +1 -0
  178. package/dist/tui/screens/tickets.js +418 -0
  179. package/dist/tui/screens/tickets.js.map +1 -0
  180. package/dist/tui/serialize.d.ts +31 -0
  181. package/dist/tui/serialize.d.ts.map +1 -0
  182. package/dist/tui/serialize.js +183 -0
  183. package/dist/tui/serialize.js.map +1 -0
  184. package/dist/tui/table-format.d.ts +11 -0
  185. package/dist/tui/table-format.d.ts.map +1 -0
  186. package/dist/tui/table-format.js +77 -0
  187. package/dist/tui/table-format.js.map +1 -0
  188. package/dist/tui/tabs.d.ts +4 -0
  189. package/dist/tui/tabs.d.ts.map +1 -0
  190. package/dist/tui/tabs.js +13 -0
  191. package/dist/tui/tabs.js.map +1 -0
  192. package/dist/tui/types.d.ts +37 -0
  193. package/dist/tui/types.d.ts.map +1 -0
  194. package/dist/tui/types.js +3 -0
  195. package/dist/tui/types.js.map +1 -0
  196. package/dist/types/client.d.ts +54 -0
  197. package/dist/types/client.d.ts.map +1 -0
  198. package/dist/types/client.js +3 -0
  199. package/dist/types/client.js.map +1 -0
  200. package/dist/types/endpoints.d.ts +29 -0
  201. package/dist/types/endpoints.d.ts.map +1 -0
  202. package/dist/types/endpoints.js +3 -0
  203. package/dist/types/endpoints.js.map +1 -0
  204. package/dist/types/profile.d.ts +29 -0
  205. package/dist/types/profile.d.ts.map +1 -0
  206. package/dist/types/profile.js +3 -0
  207. package/dist/types/profile.js.map +1 -0
  208. package/dist/utils/config-dir.d.ts +2 -0
  209. package/dist/utils/config-dir.d.ts.map +1 -0
  210. package/dist/utils/config-dir.js +23 -0
  211. package/dist/utils/config-dir.js.map +1 -0
  212. package/dist/utils/error-format.d.ts +4 -0
  213. package/dist/utils/error-format.d.ts.map +1 -0
  214. package/dist/utils/error-format.js +34 -0
  215. package/dist/utils/error-format.js.map +1 -0
  216. package/dist/utils/install-skills.d.ts +38 -0
  217. package/dist/utils/install-skills.d.ts.map +1 -0
  218. package/dist/utils/install-skills.js +117 -0
  219. package/dist/utils/install-skills.js.map +1 -0
  220. package/dist/utils/json-output.d.ts +6 -0
  221. package/dist/utils/json-output.d.ts.map +1 -0
  222. package/dist/utils/json-output.js +30 -0
  223. package/dist/utils/json-output.js.map +1 -0
  224. package/dist/utils/json.d.ts +4 -0
  225. package/dist/utils/json.d.ts.map +1 -0
  226. package/dist/utils/json.js +36 -0
  227. package/dist/utils/json.js.map +1 -0
  228. package/dist/utils/version.d.ts +2 -0
  229. package/dist/utils/version.d.ts.map +1 -0
  230. package/dist/utils/version.js +28 -0
  231. package/dist/utils/version.js.map +1 -0
  232. package/dist/workflows/fleet-insights.d.ts +122 -0
  233. package/dist/workflows/fleet-insights.d.ts.map +1 -0
  234. package/dist/workflows/fleet-insights.js +938 -0
  235. package/dist/workflows/fleet-insights.js.map +1 -0
  236. package/docs/schemas/call-envelope.v1.schema.json +140 -0
  237. package/docs/schemas/headless-frame.v1.schema.json +159 -0
  238. package/docs/schemas/inspect-deep-dive.v1.schema.json +251 -0
  239. package/docs/schemas/inspect-fleet.v1.schema.json +111 -0
  240. package/docs/schemas/report.v1.schema.json +39 -0
  241. package/package.json +75 -0
  242. package/skills/xyte-cli/SKILL.md +181 -0
  243. package/skills/xyte-cli/agents/openai.yaml +4 -0
  244. package/skills/xyte-cli/references/endpoints.md +106 -0
  245. package/skills/xyte-cli/references/headless-contract.md +96 -0
  246. package/skills/xyte-cli/references/tui-flows.md +126 -0
  247. package/skills/xyte-cli/scripts/check_headless.sh +83 -0
  248. package/skills/xyte-cli/scripts/endpoint_filters_report.sh +33 -0
  249. package/skills/xyte-cli/scripts/run_xyte_cli.sh +12 -0
  250. package/skills/xyte-cli/scripts/validate_agent_contracts.sh +72 -0
  251. package/skills/xyte-cli/scripts/validate_with_schema.js +30 -0
@@ -0,0 +1,711 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.updateErrorStormState = updateErrorStormState;
7
+ exports.runTuiApp = runTuiApp;
8
+ const blessed_1 = __importDefault(require("blessed"));
9
+ const promises_1 = require("node:timers/promises");
10
+ const layout_1 = require("./layout");
11
+ const keymap_1 = require("./keymap");
12
+ const setup_1 = require("./screens/setup");
13
+ const config_1 = require("./screens/config");
14
+ const dashboard_1 = require("./screens/dashboard");
15
+ const spaces_1 = require("./screens/spaces");
16
+ const devices_1 = require("./screens/devices");
17
+ const incidents_1 = require("./screens/incidents");
18
+ const tickets_1 = require("./screens/tickets");
19
+ const profile_store_1 = require("../secure/profile-store");
20
+ const keychain_1 = require("../secure/keychain");
21
+ const dispatch_1 = require("./dispatch");
22
+ const animation_1 = require("./animation");
23
+ const headless_renderer_1 = require("./headless-renderer");
24
+ const logo_1 = require("./assets/logo");
25
+ const readiness_1 = require("../config/readiness");
26
+ const input_controller_1 = require("./input-controller");
27
+ const runtime_1 = require("./runtime");
28
+ const logger_1 = require("./logger");
29
+ const tabs_1 = require("./tabs");
30
+ function toErrorText(error) {
31
+ if (error instanceof Error) {
32
+ return error.message;
33
+ }
34
+ return String(error);
35
+ }
36
+ async function renderStartupSequence(screen, messageBox, motionEnabled) {
37
+ const frames = (0, animation_1.startupFrames)();
38
+ if (!motionEnabled) {
39
+ const frame = frames[frames.length - 1];
40
+ messageBox.display(`${frame.banner}\n\n${frame.status}`, 1, () => undefined);
41
+ screen.render();
42
+ return;
43
+ }
44
+ for (const frame of frames) {
45
+ messageBox.display(`${frame.banner}\n\n${frame.status}`, 1, () => undefined);
46
+ screen.render();
47
+ await (0, promises_1.setTimeout)(180);
48
+ }
49
+ }
50
+ function canOpenScreen(id, readiness) {
51
+ if (id === 'setup' || id === 'config') {
52
+ return true;
53
+ }
54
+ return readiness?.state === 'ready';
55
+ }
56
+ function updateErrorStormState(state, message, now = Date.now(), windowMs = 2_000) {
57
+ if (state.message === message && now - state.startedAt <= windowMs) {
58
+ return {
59
+ message,
60
+ count: state.count + 1,
61
+ startedAt: state.startedAt
62
+ };
63
+ }
64
+ return {
65
+ message,
66
+ count: 1,
67
+ startedAt: now
68
+ };
69
+ }
70
+ async function runTuiApp(options) {
71
+ const profileStore = options.profileStore ?? new profile_store_1.FileProfileStore();
72
+ const keychain = options.keychain ?? (await (0, keychain_1.createKeychainStore)());
73
+ const motionEnabled = (0, animation_1.isMotionEnabled)({ headless: options.headless, explicitMotion: options.motionEnabled });
74
+ const debugEnabled = Boolean(options.debug || options.debugLogPath || process.env.XYTE_TUI_DEBUG === '1' || process.env.XYTE_TUI_DEBUG_LOG);
75
+ const logger = (0, logger_1.createTuiLogger)({
76
+ enabled: debugEnabled,
77
+ path: options.debugLogPath ?? process.env.XYTE_TUI_DEBUG_LOG
78
+ });
79
+ logger.log('app.start', {
80
+ headless: Boolean(options.headless),
81
+ screen: options.initialScreen ?? 'dashboard',
82
+ format: options.format ?? 'json',
83
+ tenantId: options.tenantId,
84
+ motionEnabled
85
+ });
86
+ if (options.headless) {
87
+ const requestedFormat = options.format ?? 'json';
88
+ if (requestedFormat !== 'json') {
89
+ throw new Error('Headless mode only supports JSON output.');
90
+ }
91
+ try {
92
+ await (0, headless_renderer_1.runHeadlessRenderer)({
93
+ client: options.client,
94
+ profileStore,
95
+ keychain,
96
+ screen: options.initialScreen ?? 'dashboard',
97
+ format: 'json',
98
+ motionEnabled,
99
+ follow: options.follow,
100
+ intervalMs: options.intervalMs,
101
+ tenantId: options.tenantId,
102
+ output: options.output
103
+ });
104
+ logger.log('app.headless.complete');
105
+ }
106
+ finally {
107
+ logger.close();
108
+ }
109
+ return;
110
+ }
111
+ try {
112
+ await new Promise((resolve) => {
113
+ const screen = blessed_1.default.screen({
114
+ smartCSR: true,
115
+ fullUnicode: true,
116
+ title: 'XYTE SDK TUI'
117
+ });
118
+ const layout = (0, layout_1.createLayout)(screen, { motionEnabled });
119
+ let activeScreenId = options.initialScreen ?? 'dashboard';
120
+ let pulsePhase = 0;
121
+ let readinessState;
122
+ let isPromptActive = false;
123
+ let isMessageActive = false;
124
+ let isShuttingDown = false;
125
+ let mountTransitionToken = 0;
126
+ let transitionState = 'idle';
127
+ let mountedRuntime;
128
+ let runtimeStatus = {
129
+ state: 'idle',
130
+ refreshInFlight: false,
131
+ refreshQueued: false,
132
+ staleDiscarded: 0
133
+ };
134
+ let footerStatusText = 'Ready';
135
+ let getInputState = () => ({ queueDepth: 0, droppedEvents: 0, inFlight: false });
136
+ let lastRuntimeLogLine = '';
137
+ let lastRenderCounter = 0;
138
+ let isHandlingFatalError = false;
139
+ let errorStormState = {
140
+ message: '',
141
+ count: 0,
142
+ startedAt: 0
143
+ };
144
+ logger.log('app.interactive.start', {
145
+ initialScreen: activeScreenId,
146
+ motionEnabled
147
+ });
148
+ const renderFooter = (statusText) => {
149
+ if (statusText !== undefined) {
150
+ footerStatusText = statusText;
151
+ }
152
+ const readiness = readinessState
153
+ ? `${readinessState.state}/${readinessState.connectionState} tenant=${readinessState.tenantId ?? 'none'}`
154
+ : 'status=unknown';
155
+ const inputState = getInputState();
156
+ const runtime = `refresh=${runtimeStatus.state}${runtimeStatus.refreshQueued ? '+queued' : ''} stale=${runtimeStatus.staleDiscarded} in=${inputState.queueDepth} drop=${inputState.droppedEvents} tx=${transitionState}`;
157
+ const detail = runtimeStatus.lastError ? `${footerStatusText} | err=${runtimeStatus.lastError}` : footerStatusText;
158
+ layout.footer.setContent(` @ ${readiness} | ${runtime} | ${detail}`);
159
+ screen.render();
160
+ };
161
+ const message = blessed_1.default.message({
162
+ parent: screen,
163
+ border: 'line',
164
+ width: '70%',
165
+ height: 'shrink',
166
+ top: 'center',
167
+ left: 'center',
168
+ label: ' XYTE ',
169
+ tags: true,
170
+ hidden: true
171
+ });
172
+ const promptWidget = blessed_1.default.prompt({
173
+ parent: screen,
174
+ border: 'line',
175
+ width: '70%',
176
+ height: 'shrink',
177
+ top: 'center',
178
+ left: 'center',
179
+ label: ' Input ',
180
+ tags: true,
181
+ hidden: true
182
+ });
183
+ const setMessageModalState = (active) => {
184
+ isMessageActive = active;
185
+ };
186
+ const writeErrorStderr = (source, messageText) => {
187
+ try {
188
+ process.stderr.write(`[xyte-tui] ${source}: ${messageText}\n`);
189
+ }
190
+ catch {
191
+ // best-effort stderr logging
192
+ }
193
+ };
194
+ const runPrompt = (promptText, initial = '', secret = false) => {
195
+ const promptInternals = promptWidget;
196
+ const input = promptInternals._?.input;
197
+ const prevCensor = input?.censor;
198
+ const prevSecret = input?.secret;
199
+ if (input) {
200
+ input.censor = secret;
201
+ input.secret = false;
202
+ }
203
+ isPromptActive = true;
204
+ logger.log('prompt.open', {
205
+ promptText,
206
+ secret,
207
+ hasInitial: Boolean(initial)
208
+ });
209
+ return new Promise((resolvePrompt) => {
210
+ promptWidget.input(promptText, initial, (_err, value) => {
211
+ if (input) {
212
+ input.censor = prevCensor;
213
+ input.secret = prevSecret;
214
+ }
215
+ isPromptActive = false;
216
+ logger.log('prompt.close', {
217
+ promptText,
218
+ secret,
219
+ hasValue: value !== undefined && value !== null && String(value).length > 0
220
+ });
221
+ screen.render();
222
+ resolvePrompt(value ?? undefined);
223
+ });
224
+ });
225
+ };
226
+ const refreshReadiness = async (checkConnectivity = false) => {
227
+ logger.log('readiness.refresh.start', { checkConnectivity });
228
+ readinessState = await (0, readiness_1.evaluateReadiness)({
229
+ profileStore,
230
+ keychain,
231
+ tenantId: options.tenantId,
232
+ client: options.client,
233
+ checkConnectivity
234
+ });
235
+ logger.log('readiness.refresh.complete', {
236
+ state: readinessState.state,
237
+ connectionState: readinessState.connectionState,
238
+ tenantId: readinessState.tenantId
239
+ });
240
+ renderFooter();
241
+ return readinessState;
242
+ };
243
+ let shutdownRef;
244
+ const safeShowError = (source, error) => {
245
+ const text = toErrorText(error);
246
+ logger.log('ui.error.safe', {
247
+ source,
248
+ message: text,
249
+ error
250
+ });
251
+ if (isShuttingDown) {
252
+ writeErrorStderr(source, text);
253
+ return;
254
+ }
255
+ const now = Date.now();
256
+ errorStormState = updateErrorStormState(errorStormState, text, now);
257
+ if (errorStormState.count >= 5) {
258
+ logger.log('ui.error.storm', {
259
+ source,
260
+ message: text,
261
+ count: errorStormState.count
262
+ });
263
+ writeErrorStderr(source, `error storm detected (${errorStormState.count} in 2s): ${text}`);
264
+ shutdownRef?.();
265
+ return;
266
+ }
267
+ runtimeStatus = {
268
+ ...runtimeStatus,
269
+ state: 'error',
270
+ lastError: text
271
+ };
272
+ renderFooter(`Error: ${text}`);
273
+ if (isHandlingFatalError) {
274
+ logger.log('ui.error.reentrant', {
275
+ source,
276
+ message: text
277
+ });
278
+ writeErrorStderr(source, text);
279
+ return;
280
+ }
281
+ isHandlingFatalError = true;
282
+ setMessageModalState(true);
283
+ try {
284
+ message.display(`{red-fg}Error{/red-fg}: ${text}`, 4, () => {
285
+ setMessageModalState(false);
286
+ isHandlingFatalError = false;
287
+ try {
288
+ screen.render();
289
+ }
290
+ catch (renderError) {
291
+ logger.log('ui.error.render.failure', {
292
+ source,
293
+ original: text,
294
+ renderError
295
+ });
296
+ writeErrorStderr(source, `render failure after error modal: ${toErrorText(renderError)}`);
297
+ shutdownRef?.();
298
+ }
299
+ });
300
+ }
301
+ catch (displayError) {
302
+ isHandlingFatalError = false;
303
+ setMessageModalState(false);
304
+ logger.log('ui.error.display.failure', {
305
+ source,
306
+ original: text,
307
+ displayError
308
+ });
309
+ writeErrorStderr(source, `unable to display error modal: ${toErrorText(displayError)} | original: ${text}`);
310
+ shutdownRef?.();
311
+ }
312
+ };
313
+ const context = {
314
+ screen,
315
+ client: options.client,
316
+ profileStore,
317
+ keychain,
318
+ async getActiveTenantId() {
319
+ return options.tenantId ?? (await profileStore.getData()).activeTenantId;
320
+ },
321
+ getReadiness() {
322
+ return readinessState;
323
+ },
324
+ async refreshReadiness(checkConnectivity = false) {
325
+ return refreshReadiness(checkConnectivity);
326
+ },
327
+ setStatus(text) {
328
+ renderFooter(text);
329
+ },
330
+ showError(error) {
331
+ safeShowError('context.showError', error);
332
+ },
333
+ debugLog(event, data) {
334
+ logger.log(event, data);
335
+ },
336
+ prompt(promptText, initial = '') {
337
+ return runPrompt(promptText, initial, false);
338
+ },
339
+ promptSecret(promptText, initial = '') {
340
+ return runPrompt(promptText, initial, true);
341
+ },
342
+ async confirmWrite(actionLabel, token) {
343
+ const value = await context.prompt(`Type "${token}" to confirm: ${actionLabel}`, '');
344
+ return value === token;
345
+ }
346
+ };
347
+ const screens = {
348
+ setup: (0, setup_1.createSetupScreen)(),
349
+ config: (0, config_1.createConfigScreen)(),
350
+ dashboard: (0, dashboard_1.createDashboardScreen)(),
351
+ spaces: (0, spaces_1.createSpacesScreen)(),
352
+ devices: (0, devices_1.createDevicesScreen)(),
353
+ incidents: (0, incidents_1.createIncidentsScreen)(),
354
+ tickets: (0, tickets_1.createTicketsScreen)()
355
+ };
356
+ let mounted;
357
+ const mountScreen = async (id) => {
358
+ const token = ++mountTransitionToken;
359
+ transitionState = 'switching';
360
+ logger.log('screen.mount.request', {
361
+ requested: id,
362
+ token
363
+ });
364
+ renderFooter(`Switching to ${id}...`);
365
+ const nextId = canOpenScreen(id, readinessState) ? id : 'setup';
366
+ if (nextId !== id) {
367
+ logger.log('screen.mount.redirect', {
368
+ requested: id,
369
+ redirectedTo: nextId,
370
+ readinessState: readinessState?.state
371
+ });
372
+ renderFooter(`Setup required before opening ${id}. Redirected to Setup.`);
373
+ }
374
+ mountedRuntime?.cancelPendingForUnmount();
375
+ mountedRuntime = undefined;
376
+ if (mounted) {
377
+ logger.log('screen.unmount', {
378
+ id: mounted.id
379
+ });
380
+ mounted.unmount();
381
+ }
382
+ const next = screens[nextId];
383
+ next.mount(layout.body, context);
384
+ activeScreenId = nextId;
385
+ mounted = next;
386
+ mountedRuntime = new runtime_1.ScreenRuntime({
387
+ refresh: async () => {
388
+ if (token !== mountTransitionToken || isShuttingDown) {
389
+ logger.log('screen.refresh.skip', {
390
+ id: nextId,
391
+ token,
392
+ latestToken: mountTransitionToken,
393
+ isShuttingDown
394
+ });
395
+ return;
396
+ }
397
+ logger.log('screen.refresh.start', {
398
+ id: nextId,
399
+ reason: runtimeStatus.reason
400
+ });
401
+ await next.refresh();
402
+ logger.log('screen.refresh.complete', {
403
+ id: nextId
404
+ });
405
+ },
406
+ onStatus(status) {
407
+ runtimeStatus = status;
408
+ const statusLine = JSON.stringify(status);
409
+ if (statusLine !== lastRuntimeLogLine) {
410
+ lastRuntimeLogLine = statusLine;
411
+ logger.log('screen.runtime.status', {
412
+ id: nextId,
413
+ ...status
414
+ });
415
+ }
416
+ renderFooter();
417
+ },
418
+ onError(error) {
419
+ logger.log('screen.runtime.error', {
420
+ id: nextId,
421
+ error
422
+ });
423
+ safeShowError('screen.runtime', error);
424
+ }
425
+ });
426
+ mountedRuntime.setMountToken(token);
427
+ runtimeStatus = mountedRuntime.getStatus();
428
+ layout.setActiveTab(nextId);
429
+ layout.header.setContent(` XYTE SDK TUI | ${next.title.toUpperCase()} `);
430
+ transitionState = 'idle';
431
+ next.focus?.();
432
+ logger.log('screen.mount.active', {
433
+ id: nextId,
434
+ token
435
+ });
436
+ renderFooter(`Active screen: ${next.title}`);
437
+ mountedRuntime.runRefresh('mount');
438
+ void context
439
+ .refreshReadiness(true)
440
+ .then((nextReadiness) => {
441
+ if (token !== mountTransitionToken || isShuttingDown) {
442
+ return;
443
+ }
444
+ if (nextReadiness.state !== 'ready' && !['setup', 'config'].includes(activeScreenId)) {
445
+ renderFooter(`Setup required before opening ${activeScreenId}. Redirected to Setup.`);
446
+ void mountScreen('setup');
447
+ }
448
+ })
449
+ .catch((error) => {
450
+ safeShowError('screen.mount.readiness', error);
451
+ });
452
+ };
453
+ const showHelp = () => {
454
+ const content = [
455
+ '{bold}Global shortcuts{/bold}',
456
+ ...keymap_1.GLOBAL_KEYMAP.map((item) => `- ${item.keys}: ${item.description}`),
457
+ '',
458
+ '{bold}Screen actions{/bold}',
459
+ ...keymap_1.SCREEN_ACTION_KEYMAP.map((item) => `- ${item.keys}: ${item.description}`)
460
+ ].join('\n');
461
+ setMessageModalState(true);
462
+ message.display(content, 0, () => {
463
+ setMessageModalState(false);
464
+ screen.render();
465
+ });
466
+ };
467
+ const handleGlobalKey = async (ch, key) => {
468
+ logger.log('input.global', {
469
+ key: key.name ?? key.full,
470
+ ch,
471
+ activeScreenId
472
+ });
473
+ if (key.name === 'left' || key.name === 'right') {
474
+ const target = (0, tabs_1.nextTab)(activeScreenId, key.name);
475
+ await mountScreen(target);
476
+ return;
477
+ }
478
+ if (ch === 'u') {
479
+ await mountScreen('setup');
480
+ return;
481
+ }
482
+ if (ch === 'g') {
483
+ await mountScreen('config');
484
+ return;
485
+ }
486
+ if (ch === 'd') {
487
+ await mountScreen('dashboard');
488
+ return;
489
+ }
490
+ if (ch === 's') {
491
+ await mountScreen('spaces');
492
+ return;
493
+ }
494
+ if (ch === 'v') {
495
+ await mountScreen('devices');
496
+ return;
497
+ }
498
+ if (ch === 'i') {
499
+ await mountScreen('incidents');
500
+ return;
501
+ }
502
+ if (ch === 't') {
503
+ await mountScreen('tickets');
504
+ return;
505
+ }
506
+ if (ch === 'r') {
507
+ logger.log('screen.refresh.request', {
508
+ id: activeScreenId,
509
+ via: 'global-r'
510
+ });
511
+ mountedRuntime?.runRefresh('manual');
512
+ void context
513
+ .refreshReadiness(true)
514
+ .then((nextReadiness) => {
515
+ if (isShuttingDown) {
516
+ return;
517
+ }
518
+ if (nextReadiness.state !== 'ready' && !['setup', 'config'].includes(activeScreenId)) {
519
+ renderFooter(`Setup required before opening ${activeScreenId}. Redirected to Setup.`);
520
+ void mountScreen('setup');
521
+ return;
522
+ }
523
+ renderFooter('Screen refreshed.');
524
+ })
525
+ .catch((error) => {
526
+ safeShowError('global.refresh', error);
527
+ });
528
+ return;
529
+ }
530
+ if (ch === '?') {
531
+ showHelp();
532
+ return;
533
+ }
534
+ if (key.name === 'escape') {
535
+ showHelp();
536
+ }
537
+ };
538
+ const shutdown = () => {
539
+ if (isShuttingDown) {
540
+ return;
541
+ }
542
+ isShuttingDown = true;
543
+ logger.log('app.shutdown.start', {
544
+ activeScreenId
545
+ });
546
+ try {
547
+ mountedRuntime?.cancelPendingForUnmount();
548
+ mounted?.unmount();
549
+ }
550
+ finally {
551
+ screen.destroy();
552
+ logger.log('app.shutdown.complete');
553
+ resolve();
554
+ }
555
+ };
556
+ shutdownRef = shutdown;
557
+ const inputController = (0, input_controller_1.createInputController)({
558
+ maxQueueSize: 64,
559
+ async handle(event) {
560
+ if (isShuttingDown) {
561
+ return;
562
+ }
563
+ if (event.key.full === 'C-c' || event.ch === 'q' || event.key.name === 'q') {
564
+ logger.log('input.critical', {
565
+ key: event.key.name ?? event.key.full,
566
+ full: event.key.full
567
+ });
568
+ shutdown();
569
+ return;
570
+ }
571
+ const modalActive = isPromptActive || isMessageActive;
572
+ const safeCh = modalActive ? undefined : event.ch;
573
+ const activeMounted = mounted;
574
+ const dispatchResult = await (0, dispatch_1.dispatchKeypress)({
575
+ ch: event.ch,
576
+ key: event.key,
577
+ isModalActive: modalActive,
578
+ handleArrow: activeMounted?.handleArrow
579
+ ? async (key) => {
580
+ try {
581
+ return await activeMounted.handleArrow(key);
582
+ }
583
+ catch (error) {
584
+ logger.log('input.arrow.error', {
585
+ screen: activeMounted?.id,
586
+ key,
587
+ error
588
+ });
589
+ safeShowError('input.arrow', error);
590
+ return 'handled';
591
+ }
592
+ }
593
+ : undefined,
594
+ handleScreen: activeMounted?.handleKey
595
+ ? async (ch, key) => {
596
+ try {
597
+ return await activeMounted.handleKey(ch, key);
598
+ }
599
+ catch (error) {
600
+ logger.log('input.screen.error', {
601
+ screen: activeMounted?.id,
602
+ key: key.name ?? key.full,
603
+ error
604
+ });
605
+ safeShowError('input.screen', error);
606
+ return true;
607
+ }
608
+ }
609
+ : undefined,
610
+ handleGlobal: async (ch, key) => {
611
+ try {
612
+ await handleGlobalKey(ch, key);
613
+ }
614
+ catch (error) {
615
+ logger.log('input.global.error', {
616
+ key: key.name ?? key.full,
617
+ error
618
+ });
619
+ safeShowError('input.global', error);
620
+ }
621
+ }
622
+ });
623
+ logger.log('input.dispatch', {
624
+ screen: activeMounted?.id,
625
+ key: event.key.name ?? event.key.full,
626
+ full: event.key.full,
627
+ ch: safeCh,
628
+ modalActive,
629
+ result: dispatchResult,
630
+ queueDepth: getInputState().queueDepth,
631
+ droppedEvents: getInputState().droppedEvents
632
+ });
633
+ const renderCount = Number(screen.renders ?? 0);
634
+ const renderDelta = Math.max(0, renderCount - lastRenderCounter);
635
+ lastRenderCounter = renderCount;
636
+ logger.log('nav.render.count', {
637
+ screen: activeMounted?.id,
638
+ key: event.key.name ?? event.key.full,
639
+ renderCount,
640
+ delta: renderDelta
641
+ });
642
+ renderFooter();
643
+ },
644
+ onError(error) {
645
+ logger.log('input.controller.error', { error });
646
+ safeShowError('input.controller', error);
647
+ }
648
+ });
649
+ getInputState = inputController.getState;
650
+ const onUnhandledRejection = (reason) => {
651
+ logger.log('process.unhandledRejection', { reason });
652
+ safeShowError('process.unhandledRejection', reason);
653
+ };
654
+ const onUncaughtException = (error) => {
655
+ logger.log('process.uncaughtException', { error });
656
+ safeShowError('process.uncaughtException', error);
657
+ };
658
+ process.on('unhandledRejection', onUnhandledRejection);
659
+ process.on('uncaughtException', onUncaughtException);
660
+ screen.on('keypress', (ch, key) => {
661
+ const dispatchResult = inputController.dispatch({
662
+ ch,
663
+ key,
664
+ timestamp: Date.now()
665
+ });
666
+ const modalActive = isPromptActive || isMessageActive;
667
+ logger.log('input.enqueue', {
668
+ key: key.name ?? key.full,
669
+ ch: modalActive ? undefined : ch,
670
+ modalActive,
671
+ ...dispatchResult
672
+ });
673
+ renderFooter();
674
+ });
675
+ const pulseTimer = motionEnabled
676
+ ? setInterval(() => {
677
+ pulsePhase += 1;
678
+ layout.setPulsePhase(pulsePhase);
679
+ screen.render();
680
+ }, 220)
681
+ : undefined;
682
+ void (async () => {
683
+ await renderStartupSequence(screen, message, motionEnabled);
684
+ message.hide();
685
+ layout.header.setContent(` XYTE SDK TUI | ${(0, logo_1.xyteLogoText)().split('\n')[0]} `);
686
+ const readiness = await context.refreshReadiness(true);
687
+ if (readiness.state !== 'ready') {
688
+ activeScreenId = 'setup';
689
+ }
690
+ await mountScreen(activeScreenId);
691
+ renderFooter();
692
+ })().catch((error) => {
693
+ safeShowError('app.startup', error);
694
+ });
695
+ screen.on('destroy', () => {
696
+ inputController.clear();
697
+ mountedRuntime?.cancelPendingForUnmount();
698
+ process.removeListener('unhandledRejection', onUnhandledRejection);
699
+ process.removeListener('uncaughtException', onUncaughtException);
700
+ if (pulseTimer) {
701
+ clearInterval(pulseTimer);
702
+ }
703
+ logger.close();
704
+ });
705
+ });
706
+ }
707
+ finally {
708
+ logger.close();
709
+ }
710
+ }
711
+ //# sourceMappingURL=app.js.map