@superdangerous/app-framework 4.9.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 (239) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +652 -0
  3. package/dist/api/logsRouter.d.ts +20 -0
  4. package/dist/api/logsRouter.d.ts.map +1 -0
  5. package/dist/api/logsRouter.js +515 -0
  6. package/dist/api/logsRouter.js.map +1 -0
  7. package/dist/cli/dev-server.d.ts +7 -0
  8. package/dist/cli/dev-server.d.ts.map +1 -0
  9. package/dist/cli/dev-server.js +640 -0
  10. package/dist/cli/dev-server.js.map +1 -0
  11. package/dist/cli/index.d.ts +7 -0
  12. package/dist/cli/index.d.ts.map +1 -0
  13. package/dist/cli/index.js +26 -0
  14. package/dist/cli/index.js.map +1 -0
  15. package/dist/core/StandardServer.d.ts +129 -0
  16. package/dist/core/StandardServer.d.ts.map +1 -0
  17. package/dist/core/StandardServer.js +453 -0
  18. package/dist/core/StandardServer.js.map +1 -0
  19. package/dist/core/apiResponse.d.ts +69 -0
  20. package/dist/core/apiResponse.d.ts.map +1 -0
  21. package/dist/core/apiResponse.js +127 -0
  22. package/dist/core/apiResponse.js.map +1 -0
  23. package/dist/core/healthCheck.d.ts +160 -0
  24. package/dist/core/healthCheck.d.ts.map +1 -0
  25. package/dist/core/healthCheck.js +398 -0
  26. package/dist/core/healthCheck.js.map +1 -0
  27. package/dist/core/index.d.ts +40 -0
  28. package/dist/core/index.d.ts.map +1 -0
  29. package/dist/core/index.js +40 -0
  30. package/dist/core/index.js.map +1 -0
  31. package/dist/core/logger.d.ts +117 -0
  32. package/dist/core/logger.d.ts.map +1 -0
  33. package/dist/core/logger.js +826 -0
  34. package/dist/core/logger.js.map +1 -0
  35. package/dist/core/portUtils.d.ts +71 -0
  36. package/dist/core/portUtils.d.ts.map +1 -0
  37. package/dist/core/portUtils.js +240 -0
  38. package/dist/core/portUtils.js.map +1 -0
  39. package/dist/core/storageService.d.ts +119 -0
  40. package/dist/core/storageService.d.ts.map +1 -0
  41. package/dist/core/storageService.js +405 -0
  42. package/dist/core/storageService.js.map +1 -0
  43. package/dist/desktop/bundler.d.ts +40 -0
  44. package/dist/desktop/bundler.d.ts.map +1 -0
  45. package/dist/desktop/bundler.js +176 -0
  46. package/dist/desktop/bundler.js.map +1 -0
  47. package/dist/desktop/index.d.ts +25 -0
  48. package/dist/desktop/index.d.ts.map +1 -0
  49. package/dist/desktop/index.js +15 -0
  50. package/dist/desktop/index.js.map +1 -0
  51. package/dist/desktop/native-modules.d.ts +66 -0
  52. package/dist/desktop/native-modules.d.ts.map +1 -0
  53. package/dist/desktop/native-modules.js +200 -0
  54. package/dist/desktop/native-modules.js.map +1 -0
  55. package/dist/index.d.ts +29 -0
  56. package/dist/index.d.ts.map +1 -0
  57. package/dist/index.js +39 -0
  58. package/dist/index.js.map +1 -0
  59. package/dist/logging/LogCategories.d.ts +87 -0
  60. package/dist/logging/LogCategories.d.ts.map +1 -0
  61. package/dist/logging/LogCategories.js +205 -0
  62. package/dist/logging/LogCategories.js.map +1 -0
  63. package/dist/middleware/aiErrorHandler.d.ts +31 -0
  64. package/dist/middleware/aiErrorHandler.d.ts.map +1 -0
  65. package/dist/middleware/aiErrorHandler.js +181 -0
  66. package/dist/middleware/aiErrorHandler.js.map +1 -0
  67. package/dist/middleware/auth.d.ts +101 -0
  68. package/dist/middleware/auth.d.ts.map +1 -0
  69. package/dist/middleware/auth.js +230 -0
  70. package/dist/middleware/auth.js.map +1 -0
  71. package/dist/middleware/cors.d.ts +56 -0
  72. package/dist/middleware/cors.d.ts.map +1 -0
  73. package/dist/middleware/cors.js +123 -0
  74. package/dist/middleware/cors.js.map +1 -0
  75. package/dist/middleware/errorHandler.d.ts +13 -0
  76. package/dist/middleware/errorHandler.d.ts.map +1 -0
  77. package/dist/middleware/errorHandler.js +85 -0
  78. package/dist/middleware/errorHandler.js.map +1 -0
  79. package/dist/middleware/fileUpload.d.ts +62 -0
  80. package/dist/middleware/fileUpload.d.ts.map +1 -0
  81. package/dist/middleware/fileUpload.js +175 -0
  82. package/dist/middleware/fileUpload.js.map +1 -0
  83. package/dist/middleware/health.d.ts +48 -0
  84. package/dist/middleware/health.d.ts.map +1 -0
  85. package/dist/middleware/health.js +143 -0
  86. package/dist/middleware/health.js.map +1 -0
  87. package/dist/middleware/index.d.ts +20 -0
  88. package/dist/middleware/index.d.ts.map +1 -0
  89. package/dist/middleware/index.js +18 -0
  90. package/dist/middleware/index.js.map +1 -0
  91. package/dist/middleware/openapi.d.ts +64 -0
  92. package/dist/middleware/openapi.d.ts.map +1 -0
  93. package/dist/middleware/openapi.js +258 -0
  94. package/dist/middleware/openapi.js.map +1 -0
  95. package/dist/middleware/requestLogging.d.ts +22 -0
  96. package/dist/middleware/requestLogging.d.ts.map +1 -0
  97. package/dist/middleware/requestLogging.js +61 -0
  98. package/dist/middleware/requestLogging.js.map +1 -0
  99. package/dist/middleware/session.d.ts +84 -0
  100. package/dist/middleware/session.d.ts.map +1 -0
  101. package/dist/middleware/session.js +189 -0
  102. package/dist/middleware/session.js.map +1 -0
  103. package/dist/middleware/validation.d.ts +1337 -0
  104. package/dist/middleware/validation.d.ts.map +1 -0
  105. package/dist/middleware/validation.js +483 -0
  106. package/dist/middleware/validation.js.map +1 -0
  107. package/dist/services/aiService.d.ts +180 -0
  108. package/dist/services/aiService.d.ts.map +1 -0
  109. package/dist/services/aiService.js +547 -0
  110. package/dist/services/aiService.js.map +1 -0
  111. package/dist/services/conversationStorage.d.ts +38 -0
  112. package/dist/services/conversationStorage.d.ts.map +1 -0
  113. package/dist/services/conversationStorage.js +158 -0
  114. package/dist/services/conversationStorage.js.map +1 -0
  115. package/dist/services/crossPlatformBuffer.d.ts +84 -0
  116. package/dist/services/crossPlatformBuffer.d.ts.map +1 -0
  117. package/dist/services/crossPlatformBuffer.js +246 -0
  118. package/dist/services/crossPlatformBuffer.js.map +1 -0
  119. package/dist/services/index.d.ts +17 -0
  120. package/dist/services/index.d.ts.map +1 -0
  121. package/dist/services/index.js +18 -0
  122. package/dist/services/index.js.map +1 -0
  123. package/dist/services/networkService.d.ts +81 -0
  124. package/dist/services/networkService.d.ts.map +1 -0
  125. package/dist/services/networkService.js +268 -0
  126. package/dist/services/networkService.js.map +1 -0
  127. package/dist/services/queueService.d.ts +112 -0
  128. package/dist/services/queueService.d.ts.map +1 -0
  129. package/dist/services/queueService.js +338 -0
  130. package/dist/services/queueService.js.map +1 -0
  131. package/dist/services/settingsService.d.ts +135 -0
  132. package/dist/services/settingsService.d.ts.map +1 -0
  133. package/dist/services/settingsService.js +425 -0
  134. package/dist/services/settingsService.js.map +1 -0
  135. package/dist/services/systemMonitor.d.ts +208 -0
  136. package/dist/services/systemMonitor.d.ts.map +1 -0
  137. package/dist/services/systemMonitor.js +693 -0
  138. package/dist/services/systemMonitor.js.map +1 -0
  139. package/dist/services/updateService.d.ts +78 -0
  140. package/dist/services/updateService.d.ts.map +1 -0
  141. package/dist/services/updateService.js +252 -0
  142. package/dist/services/updateService.js.map +1 -0
  143. package/dist/services/websocketEvents.d.ts +372 -0
  144. package/dist/services/websocketEvents.d.ts.map +1 -0
  145. package/dist/services/websocketEvents.js +338 -0
  146. package/dist/services/websocketEvents.js.map +1 -0
  147. package/dist/services/websocketServer.d.ts +80 -0
  148. package/dist/services/websocketServer.d.ts.map +1 -0
  149. package/dist/services/websocketServer.js +299 -0
  150. package/dist/services/websocketServer.js.map +1 -0
  151. package/dist/settings/SettingsSchema.d.ts +151 -0
  152. package/dist/settings/SettingsSchema.d.ts.map +1 -0
  153. package/dist/settings/SettingsSchema.js +424 -0
  154. package/dist/settings/SettingsSchema.js.map +1 -0
  155. package/dist/testing/TestServer.d.ts +69 -0
  156. package/dist/testing/TestServer.d.ts.map +1 -0
  157. package/dist/testing/TestServer.js +250 -0
  158. package/dist/testing/TestServer.js.map +1 -0
  159. package/dist/types/index.d.ts +137 -0
  160. package/dist/types/index.d.ts.map +1 -0
  161. package/dist/types/index.js +5 -0
  162. package/dist/types/index.js.map +1 -0
  163. package/dist/utils/appPaths.d.ts +74 -0
  164. package/dist/utils/appPaths.d.ts.map +1 -0
  165. package/dist/utils/appPaths.js +162 -0
  166. package/dist/utils/appPaths.js.map +1 -0
  167. package/dist/utils/fs-utils.d.ts +50 -0
  168. package/dist/utils/fs-utils.d.ts.map +1 -0
  169. package/dist/utils/fs-utils.js +114 -0
  170. package/dist/utils/fs-utils.js.map +1 -0
  171. package/dist/utils/index.d.ts +12 -0
  172. package/dist/utils/index.d.ts.map +1 -0
  173. package/dist/utils/index.js +10 -0
  174. package/dist/utils/index.js.map +1 -0
  175. package/dist/utils/standardConfig.d.ts +61 -0
  176. package/dist/utils/standardConfig.d.ts.map +1 -0
  177. package/dist/utils/standardConfig.js +109 -0
  178. package/dist/utils/standardConfig.js.map +1 -0
  179. package/dist/utils/startupBanner.d.ts +34 -0
  180. package/dist/utils/startupBanner.d.ts.map +1 -0
  181. package/dist/utils/startupBanner.js +169 -0
  182. package/dist/utils/startupBanner.js.map +1 -0
  183. package/dist/utils/startupLogger.d.ts +45 -0
  184. package/dist/utils/startupLogger.d.ts.map +1 -0
  185. package/dist/utils/startupLogger.js +200 -0
  186. package/dist/utils/startupLogger.js.map +1 -0
  187. package/package.json +151 -0
  188. package/src/api/logsRouter.ts +600 -0
  189. package/src/cli/dev-server.ts +803 -0
  190. package/src/cli/index.ts +31 -0
  191. package/src/core/StandardServer.ts +587 -0
  192. package/src/core/apiResponse.ts +202 -0
  193. package/src/core/healthCheck.ts +565 -0
  194. package/src/core/index.ts +80 -0
  195. package/src/core/logger.ts +1092 -0
  196. package/src/core/portUtils.ts +319 -0
  197. package/src/core/storageService.ts +595 -0
  198. package/src/desktop/bundler.ts +271 -0
  199. package/src/desktop/index.ts +18 -0
  200. package/src/desktop/native-modules.ts +289 -0
  201. package/src/index.ts +142 -0
  202. package/src/logging/LogCategories.ts +302 -0
  203. package/src/middleware/aiErrorHandler.ts +278 -0
  204. package/src/middleware/auth.ts +329 -0
  205. package/src/middleware/cors.ts +187 -0
  206. package/src/middleware/errorHandler.ts +103 -0
  207. package/src/middleware/fileUpload.ts +252 -0
  208. package/src/middleware/health.ts +206 -0
  209. package/src/middleware/index.ts +71 -0
  210. package/src/middleware/openapi.ts +305 -0
  211. package/src/middleware/requestLogging.ts +92 -0
  212. package/src/middleware/session.ts +238 -0
  213. package/src/middleware/validation.ts +603 -0
  214. package/src/services/aiService.ts +789 -0
  215. package/src/services/conversationStorage.ts +232 -0
  216. package/src/services/crossPlatformBuffer.ts +341 -0
  217. package/src/services/index.ts +47 -0
  218. package/src/services/networkService.ts +351 -0
  219. package/src/services/queueService.ts +446 -0
  220. package/src/services/settingsService.ts +549 -0
  221. package/src/services/systemMonitor.ts +936 -0
  222. package/src/services/updateService.ts +334 -0
  223. package/src/services/websocketEvents.ts +409 -0
  224. package/src/services/websocketServer.ts +394 -0
  225. package/src/settings/SettingsSchema.ts +664 -0
  226. package/src/testing/TestServer.ts +312 -0
  227. package/src/types/index.ts +154 -0
  228. package/src/utils/appPaths.ts +196 -0
  229. package/src/utils/fs-utils.ts +130 -0
  230. package/src/utils/index.ts +15 -0
  231. package/src/utils/standardConfig.ts +178 -0
  232. package/src/utils/startupBanner.ts +287 -0
  233. package/src/utils/startupLogger.ts +268 -0
  234. package/ui/dist/index.d.mts +1221 -0
  235. package/ui/dist/index.d.ts +1221 -0
  236. package/ui/dist/index.js +73 -0
  237. package/ui/dist/index.js.map +1 -0
  238. package/ui/dist/index.mjs +73 -0
  239. package/ui/dist/index.mjs.map +1 -0
@@ -0,0 +1,130 @@
1
+ /**
2
+ * File system utilities to replace fs-extra with native Node.js fs
3
+ * Provides cross-platform compatibility without ESM issues
4
+ */
5
+
6
+ import fs from "fs/promises";
7
+ import { createReadStream, createWriteStream } from "fs";
8
+ import path from "path";
9
+
10
+ /**
11
+ * Ensure a directory exists, creating it if necessary
12
+ */
13
+ export async function ensureDir(dir: string): Promise<void> {
14
+ await fs.mkdir(dir, { recursive: true });
15
+ }
16
+
17
+ /**
18
+ * Check if a path exists
19
+ */
20
+ export async function pathExists(filePath: string): Promise<boolean> {
21
+ try {
22
+ await fs.access(filePath);
23
+ return true;
24
+ } catch {
25
+ return false;
26
+ }
27
+ }
28
+
29
+ /**
30
+ * Copy a file or directory
31
+ */
32
+ export async function copy(src: string, dest: string): Promise<void> {
33
+ const stats = await fs.stat(src);
34
+
35
+ if (stats.isDirectory()) {
36
+ await ensureDir(dest);
37
+ const entries = await fs.readdir(src, { withFileTypes: true });
38
+
39
+ for (const entry of entries) {
40
+ const srcPath = path.join(src, entry.name);
41
+ const destPath = path.join(dest, entry.name);
42
+
43
+ if (entry.isDirectory()) {
44
+ await copy(srcPath, destPath);
45
+ } else {
46
+ await fs.copyFile(srcPath, destPath);
47
+ }
48
+ }
49
+ } else {
50
+ await fs.copyFile(src, dest);
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Move a file or directory
56
+ */
57
+ export async function move(
58
+ src: string,
59
+ dest: string,
60
+ options?: { overwrite?: boolean },
61
+ ): Promise<void> {
62
+ const { overwrite = true } = options || {};
63
+
64
+ if (!overwrite && (await pathExists(dest))) {
65
+ throw new Error(`Destination already exists: ${dest}`);
66
+ }
67
+
68
+ try {
69
+ await fs.rename(src, dest);
70
+ } catch (error: any) {
71
+ // If cross-device move, copy then delete
72
+ if (error.code === "EXDEV") {
73
+ await copy(src, dest);
74
+ await fs.rm(src, { recursive: true, force: true });
75
+ } else {
76
+ throw error;
77
+ }
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Remove a file or directory
83
+ */
84
+ export async function remove(filePath: string): Promise<void> {
85
+ await fs.rm(filePath, { recursive: true, force: true });
86
+ }
87
+
88
+ /**
89
+ * Read a JSON file
90
+ */
91
+ export async function readJson(filePath: string): Promise<any> {
92
+ const content = await fs.readFile(filePath, "utf8");
93
+ return JSON.parse(content);
94
+ }
95
+
96
+ /**
97
+ * Write a JSON file
98
+ */
99
+ export async function writeJson(
100
+ filePath: string,
101
+ data: any,
102
+ options?: { spaces?: number },
103
+ ): Promise<void> {
104
+ const { spaces = 2 } = options || {};
105
+ const content = JSON.stringify(data, null, spaces);
106
+ await fs.writeFile(filePath, content, "utf8");
107
+ }
108
+
109
+ /**
110
+ * Empty a directory without removing it
111
+ */
112
+ export async function emptyDir(dir: string): Promise<void> {
113
+ if (await pathExists(dir)) {
114
+ const entries = await fs.readdir(dir);
115
+ for (const entry of entries) {
116
+ await remove(path.join(dir, entry));
117
+ }
118
+ } else {
119
+ await ensureDir(dir);
120
+ }
121
+ }
122
+
123
+ // Re-export native fs methods that don't need replacement
124
+ export { fs };
125
+ export { createReadStream, createWriteStream };
126
+ export const readFile = fs.readFile.bind(fs);
127
+ export const writeFile = fs.writeFile.bind(fs);
128
+ export const readdir = fs.readdir.bind(fs);
129
+ export const stat = fs.stat.bind(fs);
130
+ export const unlink = fs.unlink;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Utilities Module Exports
3
+ */
4
+
5
+ export { default as startupLogger } from "./startupLogger.js";
6
+ export * from "../core/apiResponse.js";
7
+ export {
8
+ displayStartupBanner,
9
+ displayMinimalStartup,
10
+ } from "./startupBanner.js";
11
+ export type { StartupBannerOptions } from "./startupBanner.js";
12
+ export { loadStandardConfig, getDefaultPorts } from "./standardConfig.js";
13
+ export type { StandardAppConfig } from "./standardConfig.js";
14
+ export * from "./appPaths.js";
15
+ export * as fsUtils from "./fs-utils.js";
@@ -0,0 +1,178 @@
1
+ /**
2
+ * Standard Configuration Loader
3
+ * Provides consistent config loading across all SuperDangerous applications
4
+ */
5
+
6
+ import { readFileSync, existsSync } from "fs";
7
+ import path from "path";
8
+
9
+ export interface StandardAppConfig {
10
+ app: {
11
+ name: string;
12
+ version: string;
13
+ title: string;
14
+ description?: string;
15
+ };
16
+ server: {
17
+ port: number;
18
+ host: string;
19
+ webPort?: number;
20
+ websocketPort?: number;
21
+ };
22
+ ui?: {
23
+ theme?: "light" | "dark";
24
+ branding?: {
25
+ logo?: string;
26
+ primaryColor?: string;
27
+ secondaryColor?: string;
28
+ };
29
+ };
30
+ logging?: {
31
+ level?: string;
32
+ directory?: string;
33
+ file_pattern?: string;
34
+ maxSize?: string;
35
+ maxFiles?: string;
36
+ console_level?: string;
37
+ };
38
+ desktop?: {
39
+ enabled: boolean;
40
+ window?: {
41
+ width?: number;
42
+ height?: number;
43
+ minWidth?: number;
44
+ minHeight?: number;
45
+ };
46
+ };
47
+ defaults?: {
48
+ timeout?: number;
49
+ retryAttempts?: number;
50
+ };
51
+ [key: string]: any; // Allow app-specific config
52
+ }
53
+
54
+ /**
55
+ * Load application configuration with environment variable overrides
56
+ */
57
+ export function loadStandardConfig(dirname: string): StandardAppConfig {
58
+ // Load package.json for defaults
59
+ const packageJsonPath = path.join(dirname, "../package.json");
60
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
61
+
62
+ // Load app.json config if it exists
63
+ const configPath = path.join(dirname, "../data/config/app.json");
64
+ let appConfig: any = {};
65
+
66
+ if (existsSync(configPath)) {
67
+ appConfig = JSON.parse(readFileSync(configPath, "utf-8"));
68
+ }
69
+
70
+ // Normalize config structure (handle different formats)
71
+ const normalizedConfig: StandardAppConfig = {
72
+ app: {
73
+ name:
74
+ process.env.APP_NAME ||
75
+ appConfig.app?.name ||
76
+ packageJson.name ||
77
+ "SuperDangerous App",
78
+ version: appConfig.app?.version || packageJson.version || "1.0.0",
79
+ title:
80
+ process.env.APP_TITLE ||
81
+ appConfig.app?.title ||
82
+ appConfig.app_title || // Legacy support
83
+ packageJson.displayName || // Some packages use displayName
84
+ packageJson.name
85
+ ?.replace(/@[^/]+\//, "")
86
+ .replace(/-/g, " ")
87
+ .replace(/\b\w/g, (l: string) => l.toUpperCase()) ||
88
+ "SuperDangerous Application",
89
+ description:
90
+ process.env.APP_DESCRIPTION ||
91
+ appConfig.app?.description ||
92
+ packageJson.description,
93
+ },
94
+ server: {
95
+ port: Number(
96
+ process.env.PORT ||
97
+ process.env.API_PORT ||
98
+ appConfig.server?.port ||
99
+ appConfig.server?.api?.port ||
100
+ appConfig.api?.port ||
101
+ 8080,
102
+ ),
103
+ host:
104
+ process.env.HOST ||
105
+ appConfig.server?.host ||
106
+ appConfig.server?.api?.host ||
107
+ appConfig.api?.host ||
108
+ "0.0.0.0",
109
+ webPort: Number(
110
+ process.env.WEB_PORT ||
111
+ appConfig.server?.webPort ||
112
+ appConfig.server?.web?.port ||
113
+ appConfig.webPort ||
114
+ 5173,
115
+ ),
116
+ websocketPort: Number(
117
+ process.env.WEBSOCKET_PORT ||
118
+ appConfig.server?.websocketPort ||
119
+ appConfig.server?.websocket?.port ||
120
+ appConfig.webSocketPort ||
121
+ appConfig.server?.port ||
122
+ appConfig.api?.port,
123
+ ),
124
+ },
125
+ ui: appConfig.ui || {},
126
+ logging: {
127
+ level: process.env.LOG_LEVEL || appConfig.logging?.level || "INFO",
128
+ directory: appConfig.logging?.directory || "logs",
129
+ file_pattern: appConfig.logging?.file_pattern || "app.log",
130
+ maxSize: appConfig.logging?.maxSize || "20m",
131
+ maxFiles: appConfig.logging?.maxFiles || "14d",
132
+ console_level:
133
+ process.env.CONSOLE_LOG_LEVEL ||
134
+ appConfig.logging?.console_level ||
135
+ "INFO",
136
+ },
137
+ desktop: {
138
+ enabled:
139
+ process.env.DESKTOP === "true" || appConfig.desktop?.enabled || false,
140
+ ...appConfig.desktop,
141
+ },
142
+ defaults: appConfig.defaults || {
143
+ timeout: 5000,
144
+ retryAttempts: 3,
145
+ },
146
+ };
147
+
148
+ // Merge in any app-specific config
149
+ Object.keys(appConfig).forEach((key) => {
150
+ if (
151
+ ![
152
+ "app",
153
+ "server",
154
+ "api",
155
+ "ui",
156
+ "logging",
157
+ "desktop",
158
+ "defaults",
159
+ ].includes(key)
160
+ ) {
161
+ normalizedConfig[key] = appConfig[key];
162
+ }
163
+ });
164
+
165
+ return normalizedConfig;
166
+ }
167
+
168
+ /**
169
+ * Get default ports for an application
170
+ * Applications can override these by providing their own configuration
171
+ */
172
+ export function getDefaultPorts(_appName: string): {
173
+ api: number;
174
+ web: number;
175
+ } {
176
+ // Return sensible defaults - applications should define their own ports
177
+ return { api: 8080, web: 3000 };
178
+ }
@@ -0,0 +1,287 @@
1
+ /**
2
+ * Standardized Startup Banner for SuperDangerous Applications
3
+ */
4
+
5
+ import chalk from "chalk";
6
+ import fs from "fs";
7
+ import path from "path";
8
+
9
+ // Force colors to ensure consistent output
10
+ chalk.level = 3; // Full color support
11
+
12
+ // Get framework version dynamically
13
+ function getFrameworkVersion(): string {
14
+ // Try multiple strategies to find the framework version
15
+ const strategies = [
16
+ // Strategy 1: Check if we're in the framework itself (during development)
17
+ () => {
18
+ const localPkg = path.join(__dirname, "../../package.json");
19
+ if (fs.existsSync(localPkg)) {
20
+ const pkg = JSON.parse(fs.readFileSync(localPkg, "utf-8"));
21
+ if (pkg.name === "@superdangerous/app-framework") {
22
+ return pkg.version;
23
+ }
24
+ }
25
+ return null;
26
+ },
27
+ // Strategy 2: Check node_modules in current working directory
28
+ () => {
29
+ const nodeModulesPath = path.join(
30
+ process.cwd(),
31
+ "node_modules",
32
+ "@superdangerous",
33
+ "app-framework",
34
+ "package.json",
35
+ );
36
+ if (fs.existsSync(nodeModulesPath)) {
37
+ const pkg = JSON.parse(fs.readFileSync(nodeModulesPath, "utf-8"));
38
+ return pkg.version;
39
+ }
40
+ return null;
41
+ },
42
+ // Strategy 3: Try require.resolve to find the module
43
+ () => {
44
+ try {
45
+ const pkgPath = require.resolve(
46
+ "@superdangerous/app-framework/package.json",
47
+ { paths: [process.cwd()] },
48
+ );
49
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
50
+ return pkg.version;
51
+ } catch {
52
+ return null;
53
+ }
54
+ },
55
+ ];
56
+
57
+ for (const strategy of strategies) {
58
+ try {
59
+ const version = strategy();
60
+ if (version) return version;
61
+ } catch {
62
+ // Try next strategy
63
+ }
64
+ }
65
+
66
+ return "unknown";
67
+ }
68
+
69
+ const version = getFrameworkVersion();
70
+
71
+ export interface BannerOptions {
72
+ appName: string;
73
+ appVersion: string;
74
+ environment?: string;
75
+ port?: number;
76
+ additionalInfo?: string[];
77
+ showTips?: boolean;
78
+ showCredits?: boolean;
79
+ color?: "blue" | "green" | "yellow" | "red";
80
+ }
81
+
82
+ export interface StartupBannerOptions extends BannerOptions {
83
+ appName: string;
84
+ appVersion: string;
85
+ packageName?: string; // Optional package name
86
+ description?: string;
87
+ port: number; // API port
88
+ webPort?: number; // Optional separate web UI port
89
+ webSocketPort?: number; // Optional WebSocket port
90
+ environment?: string;
91
+ startTime?: number;
92
+ }
93
+
94
+ /**
95
+ * Display a standardized startup banner for SuperDangerous applications
96
+ * All apps should use this exact same format for consistency
97
+ */
98
+ export function displayStartupBanner(options: StartupBannerOptions): void {
99
+ const {
100
+ appName,
101
+ appVersion,
102
+ description,
103
+ port,
104
+ webPort, // Use separate web port if provided
105
+ webSocketPort, // WebSocket port
106
+ environment = process.env.NODE_ENV || "development",
107
+ startTime,
108
+ } = options;
109
+
110
+ const width = 50; // Reduced width to match horizontal dividers
111
+ const border = "─".repeat(width); // Full width for top/bottom borders
112
+
113
+ // Calculate startup time if provided
114
+ const startupTime = startTime
115
+ ? `${((Date.now() - startTime) / 1000).toFixed(1)}s`
116
+ : "0.0s";
117
+
118
+ // Helper to create a line with proper padding
119
+ const makeLine = (
120
+ content: string,
121
+ align: "center" | "left" = "center",
122
+ ): string => {
123
+ // eslint-disable-next-line no-control-regex
124
+ const visibleLength = content.replace(/\x1b\[[0-9;]*m/g, "").length; // Strip ANSI codes for length calculation
125
+ if (align === "center") {
126
+ const leftPad = Math.max(0, Math.floor((width - visibleLength) / 2));
127
+ const rightPad = Math.max(0, width - visibleLength - leftPad);
128
+ return (
129
+ chalk.gray("│") +
130
+ " ".repeat(leftPad) +
131
+ content +
132
+ " ".repeat(rightPad) +
133
+ chalk.gray("│")
134
+ );
135
+ } else {
136
+ const padding = Math.max(0, width - visibleLength);
137
+ return chalk.gray("│") + content + " ".repeat(padding) + chalk.gray("│");
138
+ }
139
+ };
140
+
141
+ // Helper to wrap text
142
+ const wrapText = (text: string, maxWidth: number): string[] => {
143
+ const words = text.split(" ");
144
+ const lines: string[] = [];
145
+ let currentLine = "";
146
+
147
+ for (const word of words) {
148
+ if ((currentLine + " " + word).trim().length <= maxWidth) {
149
+ currentLine = currentLine ? `${currentLine} ${word}` : word;
150
+ } else {
151
+ if (currentLine) lines.push(currentLine);
152
+ currentLine = word;
153
+ }
154
+ }
155
+ if (currentLine) lines.push(currentLine);
156
+
157
+ return lines;
158
+ };
159
+
160
+ const emptyLine = chalk.gray("│") + " ".repeat(width) + chalk.gray("│");
161
+ const separator =
162
+ chalk.gray("│ ") + chalk.gray("─".repeat(width - 2)) + chalk.gray(" │");
163
+
164
+ // Build the banner
165
+ console.log("");
166
+ console.log(chalk.gray("╭" + border + "╮"));
167
+ console.log(chalk.gray(emptyLine));
168
+
169
+ // App name in light gray
170
+ console.log(makeLine(chalk.gray(appName)));
171
+ console.log(emptyLine);
172
+
173
+ // Version in amber to match Ctrl+C
174
+ console.log(makeLine(chalk.yellow(`v${appVersion}`)));
175
+ console.log(emptyLine);
176
+
177
+ // Description (wrapped if needed)
178
+ if (description) {
179
+ const wrappedLines = wrapText(description, width - 4);
180
+ for (const line of wrappedLines) {
181
+ console.log(makeLine(line));
182
+ }
183
+ console.log(emptyLine);
184
+ }
185
+
186
+ console.log(separator);
187
+ console.log(emptyLine);
188
+
189
+ // URLs - no emojis, keys dark gray, values light gray
190
+ if (webPort && webPort !== port) {
191
+ // Both web UI and API are configured on different ports
192
+ console.log(
193
+ makeLine(
194
+ `${chalk.gray(" Web UI :")} ${chalk.gray(`http://localhost:${webPort}`)}`,
195
+ "left",
196
+ ),
197
+ );
198
+ console.log(
199
+ makeLine(
200
+ `${chalk.gray(" API :")} ${chalk.gray(`http://localhost:${port}/api`)}`,
201
+ "left",
202
+ ),
203
+ );
204
+ } else if (webPort === port) {
205
+ // Web UI and API share the same port (production mode)
206
+ console.log(
207
+ makeLine(
208
+ `${chalk.gray(" Web UI :")} ${chalk.gray(`http://localhost:${port}`)}`,
209
+ "left",
210
+ ),
211
+ );
212
+ console.log(
213
+ makeLine(
214
+ `${chalk.gray(" API :")} ${chalk.gray(`http://localhost:${port}/api`)}`,
215
+ "left",
216
+ ),
217
+ );
218
+ } else {
219
+ // API-only mode
220
+ console.log(
221
+ makeLine(
222
+ `${chalk.gray(" API Server :")} ${chalk.gray(`http://localhost:${port}`)}`,
223
+ "left",
224
+ ),
225
+ );
226
+ console.log(
227
+ makeLine(
228
+ `${chalk.gray(" Endpoints :")} ${chalk.gray(`http://localhost:${port}/api/*`)}`,
229
+ "left",
230
+ ),
231
+ );
232
+ }
233
+ console.log(
234
+ makeLine(
235
+ `${chalk.gray(" WebSocket :")} ${chalk.gray(`ws://localhost:${webSocketPort || port}`)}`,
236
+ "left",
237
+ ),
238
+ );
239
+
240
+ console.log(emptyLine);
241
+ console.log(separator);
242
+ console.log(emptyLine);
243
+
244
+ // Status lines
245
+ console.log(
246
+ makeLine(`${chalk.gray(" Ready in")} ${chalk.green(startupTime)}`, "left"),
247
+ );
248
+ console.log(
249
+ makeLine(
250
+ `${chalk.gray(" Environment:")} ${chalk.gray(environment)}`,
251
+ "left",
252
+ ),
253
+ );
254
+ console.log(
255
+ makeLine(
256
+ `${chalk.gray(" Framework:")} ${chalk.gray(`@superdangerous/app-framework v${version}`)}`,
257
+ "left",
258
+ ),
259
+ );
260
+
261
+ console.log(emptyLine);
262
+ console.log(separator);
263
+ console.log(emptyLine);
264
+
265
+ console.log(
266
+ makeLine(
267
+ `${chalk.gray(" Press")} ${chalk.yellow("Ctrl+C")} ${chalk.gray("to stop")}`,
268
+ "left",
269
+ ),
270
+ );
271
+
272
+ console.log(chalk.gray(emptyLine));
273
+ console.log(chalk.gray("╰" + border + "╯"));
274
+ console.log("");
275
+ }
276
+
277
+ /**
278
+ * Display a minimal startup message (for silent mode)
279
+ */
280
+ export function displayMinimalStartup(appName: string, port: number): void {
281
+ console.log(
282
+ chalk.green("✓"),
283
+ chalk.bold(appName),
284
+ "started on port",
285
+ chalk.cyan(port),
286
+ );
287
+ }