nebula-notebook 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 (97) hide show
  1. package/README.md +222 -0
  2. package/bin/nebula-notebook.js +33 -0
  3. package/dist/assets/index-C1h_sArD.css +32 -0
  4. package/dist/assets/index-CDSTBon8.js +658 -0
  5. package/dist/favicon.svg +11 -0
  6. package/dist/index.html +73 -0
  7. package/node-server/dist/app.d.ts +5 -0
  8. package/node-server/dist/app.js +38 -0
  9. package/node-server/dist/auth/auth-middleware.d.ts +24 -0
  10. package/node-server/dist/auth/auth-middleware.js +276 -0
  11. package/node-server/dist/auth/auth-service.d.ts +84 -0
  12. package/node-server/dist/auth/auth-service.js +265 -0
  13. package/node-server/dist/auth/index.d.ts +3 -0
  14. package/node-server/dist/auth/index.js +8 -0
  15. package/node-server/dist/cluster/client-registration.d.ts +43 -0
  16. package/node-server/dist/cluster/client-registration.js +217 -0
  17. package/node-server/dist/cluster/cluster-secret.d.ts +11 -0
  18. package/node-server/dist/cluster/cluster-secret.js +90 -0
  19. package/node-server/dist/cluster/kernel-proxy.d.ts +100 -0
  20. package/node-server/dist/cluster/kernel-proxy.js +361 -0
  21. package/node-server/dist/cluster/server-registry.d.ts +109 -0
  22. package/node-server/dist/cluster/server-registry.js +217 -0
  23. package/node-server/dist/config/output-limits.d.ts +7 -0
  24. package/node-server/dist/config/output-limits.js +10 -0
  25. package/node-server/dist/discovery/discovery-service.d.ts +198 -0
  26. package/node-server/dist/discovery/discovery-service.js +811 -0
  27. package/node-server/dist/discovery/index.d.ts +5 -0
  28. package/node-server/dist/discovery/index.js +21 -0
  29. package/node-server/dist/discovery/types.d.ts +48 -0
  30. package/node-server/dist/discovery/types.js +24 -0
  31. package/node-server/dist/fs/fs-service.d.ts +218 -0
  32. package/node-server/dist/fs/fs-service.js +1422 -0
  33. package/node-server/dist/fs/index.d.ts +5 -0
  34. package/node-server/dist/fs/index.js +21 -0
  35. package/node-server/dist/fs/types.d.ts +132 -0
  36. package/node-server/dist/fs/types.js +5 -0
  37. package/node-server/dist/index.d.ts +13 -0
  38. package/node-server/dist/index.js +556 -0
  39. package/node-server/dist/kernel/default-kernel.d.ts +5 -0
  40. package/node-server/dist/kernel/default-kernel.js +138 -0
  41. package/node-server/dist/kernel/index.d.ts +7 -0
  42. package/node-server/dist/kernel/index.js +23 -0
  43. package/node-server/dist/kernel/kernel-service.d.ts +290 -0
  44. package/node-server/dist/kernel/kernel-service.js +1714 -0
  45. package/node-server/dist/kernel/kernelspec.d.ts +29 -0
  46. package/node-server/dist/kernel/kernelspec.js +210 -0
  47. package/node-server/dist/kernel/session-store.d.ts +87 -0
  48. package/node-server/dist/kernel/session-store.js +303 -0
  49. package/node-server/dist/kernel/types.d.ts +143 -0
  50. package/node-server/dist/kernel/types.js +17 -0
  51. package/node-server/dist/llm/index.d.ts +5 -0
  52. package/node-server/dist/llm/index.js +21 -0
  53. package/node-server/dist/llm/llm-service.d.ts +77 -0
  54. package/node-server/dist/llm/llm-service.js +454 -0
  55. package/node-server/dist/llm/types.d.ts +40 -0
  56. package/node-server/dist/llm/types.js +15 -0
  57. package/node-server/dist/notebook/cell-metadata.d.ts +27 -0
  58. package/node-server/dist/notebook/cell-metadata.js +76 -0
  59. package/node-server/dist/notebook/headless-handler.d.ts +127 -0
  60. package/node-server/dist/notebook/headless-handler.js +1530 -0
  61. package/node-server/dist/notebook/notebook-websocket.d.ts +12 -0
  62. package/node-server/dist/notebook/notebook-websocket.js +103 -0
  63. package/node-server/dist/notebook/operation-router.d.ts +115 -0
  64. package/node-server/dist/notebook/operation-router.js +641 -0
  65. package/node-server/dist/notebook/undoRedoManager.d.ts +194 -0
  66. package/node-server/dist/notebook/undoRedoManager.js +558 -0
  67. package/node-server/dist/output/display-data.d.ts +14 -0
  68. package/node-server/dist/output/display-data.js +134 -0
  69. package/node-server/dist/resources/resource-service.d.ts +69 -0
  70. package/node-server/dist/resources/resource-service.js +363 -0
  71. package/node-server/dist/routes/auth.d.ts +5 -0
  72. package/node-server/dist/routes/auth.js +61 -0
  73. package/node-server/dist/routes/cluster.d.ts +7 -0
  74. package/node-server/dist/routes/cluster.js +94 -0
  75. package/node-server/dist/routes/fs.d.ts +7 -0
  76. package/node-server/dist/routes/fs.js +392 -0
  77. package/node-server/dist/routes/kernel.d.ts +13 -0
  78. package/node-server/dist/routes/kernel.js +637 -0
  79. package/node-server/dist/routes/llm.d.ts +8 -0
  80. package/node-server/dist/routes/llm.js +105 -0
  81. package/node-server/dist/routes/notebook.d.ts +10 -0
  82. package/node-server/dist/routes/notebook.js +335 -0
  83. package/node-server/dist/routes/python.d.ts +8 -0
  84. package/node-server/dist/routes/python.js +187 -0
  85. package/node-server/dist/routes/resources.d.ts +7 -0
  86. package/node-server/dist/routes/resources.js +77 -0
  87. package/node-server/dist/scripts/show-auth-qr.d.ts +1 -0
  88. package/node-server/dist/scripts/show-auth-qr.js +81 -0
  89. package/node-server/dist/terminal/pty-manager.d.ts +100 -0
  90. package/node-server/dist/terminal/pty-manager.js +246 -0
  91. package/node-server/dist/terminal/server.d.ts +19 -0
  92. package/node-server/dist/terminal/server.js +254 -0
  93. package/node-server/dist/terminal/types.d.ts +50 -0
  94. package/node-server/dist/terminal/types.js +9 -0
  95. package/node-server/package.json +45 -0
  96. package/package.json +99 -0
  97. package/scripts/postinstall.cjs +26 -0
@@ -0,0 +1,11 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
2
+ <defs>
3
+ <linearGradient id="nebula" x1="0%" y1="0%" x2="100%" y2="100%">
4
+ <stop offset="0%" style="stop-color:#8b5cf6"/>
5
+ <stop offset="50%" style="stop-color:#6366f1"/>
6
+ <stop offset="100%" style="stop-color:#3b82f6"/>
7
+ </linearGradient>
8
+ </defs>
9
+ <rect width="32" height="32" rx="6" fill="url(#nebula)"/>
10
+ <path d="M8 10h16M8 16h12M8 22h14" stroke="white" stroke-width="2.5" stroke-linecap="round"/>
11
+ </svg>
@@ -0,0 +1,73 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg" id="favicon" />
7
+ <title>Nebula Notebook</title>
8
+ <style>
9
+ /* Custom scrollbar for a cleaner look */
10
+ ::-webkit-scrollbar {
11
+ width: 8px;
12
+ height: 8px;
13
+ }
14
+ ::-webkit-scrollbar-track {
15
+ background: transparent;
16
+ }
17
+ ::-webkit-scrollbar-thumb {
18
+ background: #cbd5e1;
19
+ border-radius: 4px;
20
+ }
21
+ ::-webkit-scrollbar-thumb:hover {
22
+ background: #94a3b8;
23
+ }
24
+ .cm-editor { font-family: 'Fira Code', monospace; }
25
+
26
+ /* Pandas Dataframe Styling */
27
+ .dataframe {
28
+ font-size: 0.85rem;
29
+ border-collapse: collapse;
30
+ border: 1px solid #e2e8f0;
31
+ width: 100%;
32
+ max-width: 100%;
33
+ overflow-x: auto;
34
+ display: block;
35
+ }
36
+ .dataframe th {
37
+ background-color: #f8fafc;
38
+ font-weight: 600;
39
+ text-align: left;
40
+ padding: 0.5rem;
41
+ border-bottom: 2px solid #e2e8f0;
42
+ color: #475569;
43
+ }
44
+ .dataframe td {
45
+ padding: 0.5rem;
46
+ border-bottom: 1px solid #e2e8f0;
47
+ color: #334155;
48
+ }
49
+ .dataframe tr:nth-child(even) {
50
+ background-color: #fcfcfc;
51
+ }
52
+ .dataframe tr:hover {
53
+ background-color: #f1f5f9;
54
+ }
55
+ </style>
56
+
57
+ <script type="importmap">
58
+ {
59
+ "imports": {
60
+ "lucide-react": "https://esm.sh/lucide-react@^0.561.0",
61
+ "react/": "https://esm.sh/react@^19.2.3/",
62
+ "react": "https://esm.sh/react@^19.2.3",
63
+ "react-dom/": "https://esm.sh/react-dom@^19.2.3/"
64
+ }
65
+ }
66
+ </script>
67
+ <script type="module" crossorigin src="/assets/index-CDSTBon8.js"></script>
68
+ <link rel="stylesheet" crossorigin href="/assets/index-C1h_sArD.css">
69
+ </head>
70
+ <body class="bg-slate-50 text-slate-900 antialiased">
71
+ <div id="root"></div>
72
+ </body>
73
+ </html>
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Fastify App Setup - Shared middleware and configuration
3
+ */
4
+ import { FastifyInstance } from 'fastify';
5
+ export declare function createApp(): FastifyInstance;
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ /**
3
+ * Fastify App Setup - Shared middleware and configuration
4
+ */
5
+ var __importDefault = (this && this.__importDefault) || function (mod) {
6
+ return (mod && mod.__esModule) ? mod : { "default": mod };
7
+ };
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.createApp = createApp;
10
+ const fastify_1 = __importDefault(require("fastify"));
11
+ function createApp() {
12
+ const bodyLimit = process.env.NEBULA_BODY_LIMIT ||
13
+ process.env.NEBULA_MAX_BODY_SIZE ||
14
+ '200mb';
15
+ const app = (0, fastify_1.default)({
16
+ bodyLimit: parseBodyLimit(bodyLimit),
17
+ });
18
+ return app;
19
+ }
20
+ /**
21
+ * Parse a human-readable body limit string (e.g., '200mb', '1gb') to bytes.
22
+ */
23
+ function parseBodyLimit(limit) {
24
+ const match = limit.match(/^(\d+(?:\.\d+)?)\s*(b|kb|mb|gb|tb)?$/i);
25
+ if (!match) {
26
+ return 200 * 1024 * 1024; // default 200mb
27
+ }
28
+ const value = parseFloat(match[1]);
29
+ const unit = (match[2] || 'b').toLowerCase();
30
+ const multipliers = {
31
+ b: 1,
32
+ kb: 1024,
33
+ mb: 1024 * 1024,
34
+ gb: 1024 * 1024 * 1024,
35
+ tb: 1024 * 1024 * 1024 * 1024,
36
+ };
37
+ return Math.floor(value * (multipliers[unit] || 1));
38
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Auth Middleware - Protect routes and WebSocket connections
3
+ *
4
+ * Public routes (no auth required):
5
+ * - /api/health
6
+ * - /api/ready
7
+ * - /api/auth/*
8
+ */
9
+ import { FastifyRequest, FastifyReply } from 'fastify';
10
+ import { IncomingMessage } from 'http';
11
+ /**
12
+ * Persist a session token to disk so MCP servers and CLI tools
13
+ * can piggyback on the browser's authentication.
14
+ */
15
+ export declare function persistSessionToken(token: string): void;
16
+ /**
17
+ * Fastify onRequest hook for authentication
18
+ */
19
+ export declare function authMiddleware(request: FastifyRequest, reply: FastifyReply): Promise<void>;
20
+ /**
21
+ * WebSocket authentication middleware
22
+ * Returns true if the connection is authenticated
23
+ */
24
+ export declare function authWebSocketMiddleware(request: IncomingMessage): boolean;
@@ -0,0 +1,276 @@
1
+ "use strict";
2
+ /**
3
+ * Auth Middleware - Protect routes and WebSocket connections
4
+ *
5
+ * Public routes (no auth required):
6
+ * - /api/health
7
+ * - /api/ready
8
+ * - /api/auth/*
9
+ */
10
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ var desc = Object.getOwnPropertyDescriptor(m, k);
13
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
14
+ desc = { enumerable: true, get: function() { return m[k]; } };
15
+ }
16
+ Object.defineProperty(o, k2, desc);
17
+ }) : (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ o[k2] = m[k];
20
+ }));
21
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
22
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
23
+ }) : function(o, v) {
24
+ o["default"] = v;
25
+ });
26
+ var __importStar = (this && this.__importStar) || (function () {
27
+ var ownKeys = function(o) {
28
+ ownKeys = Object.getOwnPropertyNames || function (o) {
29
+ var ar = [];
30
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
31
+ return ar;
32
+ };
33
+ return ownKeys(o);
34
+ };
35
+ return function (mod) {
36
+ if (mod && mod.__esModule) return mod;
37
+ var result = {};
38
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
39
+ __setModuleDefault(result, mod);
40
+ return result;
41
+ };
42
+ })();
43
+ Object.defineProperty(exports, "__esModule", { value: true });
44
+ exports.persistSessionToken = persistSessionToken;
45
+ exports.authMiddleware = authMiddleware;
46
+ exports.authWebSocketMiddleware = authWebSocketMiddleware;
47
+ const url_1 = require("url");
48
+ const fs = __importStar(require("fs"));
49
+ const path = __importStar(require("path"));
50
+ const os = __importStar(require("os"));
51
+ const auth_service_1 = require("./auth-service");
52
+ const cluster_secret_1 = require("../cluster/cluster-secret");
53
+ const SESSION_TOKEN_PATH = path.join(os.homedir(), '.nebula', 'session-token');
54
+ /**
55
+ * Persist a session token to disk so MCP servers and CLI tools
56
+ * can piggyback on the browser's authentication.
57
+ */
58
+ function persistSessionToken(token) {
59
+ try {
60
+ const dir = path.dirname(SESSION_TOKEN_PATH);
61
+ if (!fs.existsSync(dir))
62
+ fs.mkdirSync(dir, { recursive: true });
63
+ fs.writeFileSync(SESSION_TOKEN_PATH, token, { mode: 0o600 });
64
+ }
65
+ catch {
66
+ // Non-critical — MCP just won't be able to auto-auth
67
+ }
68
+ }
69
+ /**
70
+ * Read the persisted session token (written when browser authenticates).
71
+ */
72
+ function readSessionToken() {
73
+ try {
74
+ if (!fs.existsSync(SESSION_TOKEN_PATH))
75
+ return undefined;
76
+ const token = fs.readFileSync(SESSION_TOKEN_PATH, 'utf-8').trim();
77
+ return token || undefined;
78
+ }
79
+ catch {
80
+ return undefined;
81
+ }
82
+ }
83
+ // Routes that don't require authentication
84
+ const PUBLIC_ROUTES = [
85
+ '/api/health',
86
+ '/api/ready',
87
+ '/api/auth/status',
88
+ '/api/auth/verify',
89
+ ];
90
+ const CLUSTER_SECRET_HEADER = 'x-nebula-cluster-secret';
91
+ function isClientMode() {
92
+ const explicit = process.env.NEBULA_CLIENT_MODE;
93
+ if (explicit !== undefined) {
94
+ return explicit === 'true';
95
+ }
96
+ return !!process.env.NEBULA_MAIN_SERVER;
97
+ }
98
+ function getClusterSecret() {
99
+ return process.env.NEBULA_CLUSTER_SECRET || (0, cluster_secret_1.readClusterSecret)() || null;
100
+ }
101
+ /**
102
+ * Extract auth token from request
103
+ * Checks Authorization header (Bearer token) and query parameter
104
+ */
105
+ function extractToken(req) {
106
+ // Check Authorization header
107
+ const authHeader = req.headers.authorization;
108
+ if (authHeader?.startsWith('Bearer ')) {
109
+ return authHeader.slice(7);
110
+ }
111
+ // Check query parameter (for WebSocket and MCP connections)
112
+ // FastifyRequest has parsed query, IncomingMessage needs URL parsing
113
+ if ('query' in req && typeof req.query === 'object' && req.query?.token) {
114
+ return req.query.token;
115
+ }
116
+ // Fallback: parse URL manually (for IncomingMessage / WebSocket upgrade)
117
+ const url = req.url;
118
+ if (url) {
119
+ const parsed = (0, url_1.parse)(url, true);
120
+ const token = parsed.query.token;
121
+ if (typeof token === 'string') {
122
+ return token;
123
+ }
124
+ }
125
+ return undefined;
126
+ }
127
+ function extractClusterSecret(req) {
128
+ const headers = req.headers;
129
+ const value = headers[CLUSTER_SECRET_HEADER];
130
+ if (Array.isArray(value)) {
131
+ return value[0];
132
+ }
133
+ return value;
134
+ }
135
+ function hasValidClusterSecret(req) {
136
+ const clusterSecret = getClusterSecret();
137
+ if (!clusterSecret) {
138
+ return false;
139
+ }
140
+ const provided = extractClusterSecret(req);
141
+ return Boolean(provided && provided === clusterSecret);
142
+ }
143
+ function isClusterRoute(pathname) {
144
+ return pathname.startsWith('/api/servers') || pathname.startsWith('/servers');
145
+ }
146
+ /**
147
+ * Check if a path is a public route
148
+ */
149
+ function isPublicRoute(pathname) {
150
+ // Exact matches
151
+ if (PUBLIC_ROUTES.includes(pathname)) {
152
+ return true;
153
+ }
154
+ // Static files (frontend assets)
155
+ if (!pathname.startsWith('/api/')) {
156
+ return true;
157
+ }
158
+ return false;
159
+ }
160
+ /**
161
+ * Fastify onRequest hook for authentication
162
+ */
163
+ async function authMiddleware(request, reply) {
164
+ // Reconstruct the full pathname including the prefix
165
+ // Fastify strips the prefix when registering under /api, so we need the raw URL
166
+ const pathname = request.url.split('?')[0];
167
+ if (isClientMode()) {
168
+ // Allow public routes regardless
169
+ if (isPublicRoute(pathname)) {
170
+ return;
171
+ }
172
+ if (!getClusterSecret()) {
173
+ return reply.code(503).send({
174
+ error: 'cluster_secret_required',
175
+ message: 'Cluster secret required for client mode. Set NEBULA_CLUSTER_SECRET.',
176
+ });
177
+ }
178
+ if (!hasValidClusterSecret(request)) {
179
+ return reply.code(403).send({
180
+ error: 'cluster_auth_required',
181
+ message: 'Cluster authentication required.',
182
+ });
183
+ }
184
+ return;
185
+ }
186
+ if (isClusterRoute(pathname) && hasValidClusterSecret(request)) {
187
+ return;
188
+ }
189
+ if (auth_service_1.authService.isAuthDisabled()) {
190
+ return;
191
+ }
192
+ // Allow public routes
193
+ if (isPublicRoute(pathname)) {
194
+ return;
195
+ }
196
+ // Check if auth is configured
197
+ if (!auth_service_1.authService.isSetupComplete()) {
198
+ // During setup, only allow auth routes
199
+ return reply.code(503).send({
200
+ error: 'setup_required',
201
+ message: '2FA setup not complete. Check server terminal for QR code.',
202
+ });
203
+ }
204
+ // Extract and validate token
205
+ let token = extractToken(request);
206
+ // Fallback: if no token in request, check the persisted session token file.
207
+ // This allows MCP servers and CLI tools to piggyback on the browser's auth
208
+ // without needing their own TOTP flow.
209
+ if (!token) {
210
+ token = readSessionToken();
211
+ }
212
+ if (!token) {
213
+ return reply.code(401).send({
214
+ error: 'auth_required',
215
+ message: 'Authentication required. Please log in.',
216
+ });
217
+ }
218
+ const payload = auth_service_1.authService.validateToken(token);
219
+ if (!payload) {
220
+ return reply.code(401).send({
221
+ error: 'invalid_token',
222
+ message: 'Invalid or expired token. Please log in again.',
223
+ });
224
+ }
225
+ // Persist valid tokens from request headers so MCP/CLI can piggyback.
226
+ // Only write once (when file doesn't exist or token differs).
227
+ const requestToken = extractToken(request);
228
+ if (requestToken) {
229
+ const existing = readSessionToken();
230
+ if (existing !== requestToken) {
231
+ persistSessionToken(requestToken);
232
+ }
233
+ }
234
+ // Token is valid, continue (simply return)
235
+ }
236
+ /**
237
+ * WebSocket authentication middleware
238
+ * Returns true if the connection is authenticated
239
+ */
240
+ function authWebSocketMiddleware(request) {
241
+ if (isClientMode()) {
242
+ const clusterSecret = getClusterSecret();
243
+ if (!clusterSecret) {
244
+ console.log('[Auth] WebSocket rejected - cluster secret not configured');
245
+ return false;
246
+ }
247
+ const provided = extractClusterSecret(request);
248
+ if (provided !== clusterSecret) {
249
+ console.log('[Auth] WebSocket rejected - invalid cluster secret');
250
+ return false;
251
+ }
252
+ return true;
253
+ }
254
+ if (auth_service_1.authService.isAuthDisabled()) {
255
+ return true;
256
+ }
257
+ const url = request.url || '';
258
+ const pathname = (0, url_1.parse)(url).pathname || '';
259
+ // Check if auth is configured
260
+ if (!auth_service_1.authService.isSetupComplete()) {
261
+ console.log('[Auth] WebSocket rejected - setup not complete');
262
+ return false;
263
+ }
264
+ // Extract and validate token from query parameter
265
+ const token = extractToken(request);
266
+ if (!token) {
267
+ console.log('[Auth] WebSocket rejected - no token provided');
268
+ return false;
269
+ }
270
+ const payload = auth_service_1.authService.validateToken(token);
271
+ if (!payload) {
272
+ console.log('[Auth] WebSocket rejected - invalid token');
273
+ return false;
274
+ }
275
+ return true;
276
+ }
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Auth Service - TOTP-based 2FA authentication
3
+ *
4
+ * Handles:
5
+ * - TOTP secret generation and verification
6
+ * - JWT session token management
7
+ * - Auth config persistence (~/.nebula/auth.json)
8
+ */
9
+ export interface AuthConfig {
10
+ totpSecret: string;
11
+ jwtSecret: string;
12
+ setupComplete: boolean;
13
+ createdAt: number;
14
+ }
15
+ export interface JWTPayload {
16
+ iat: number;
17
+ exp: number;
18
+ trusted: boolean;
19
+ }
20
+ export interface AuthStatus {
21
+ configured: boolean;
22
+ authenticated: boolean;
23
+ }
24
+ export interface VerifyResult {
25
+ success: boolean;
26
+ token?: string;
27
+ error?: string;
28
+ }
29
+ declare class AuthService {
30
+ private config;
31
+ private initialized;
32
+ private failedAttempts;
33
+ private disabled;
34
+ /**
35
+ * Disable authentication (for local/dev usage only)
36
+ */
37
+ disableAuth(): void;
38
+ isAuthDisabled(): boolean;
39
+ /**
40
+ * Initialize the auth service
41
+ * Returns true if setup is needed (first run)
42
+ */
43
+ initialize(): Promise<boolean>;
44
+ /**
45
+ * Generate a secure random JWT secret
46
+ */
47
+ private generateJWTSecret;
48
+ /**
49
+ * Save config to disk with secure permissions
50
+ */
51
+ private saveConfig;
52
+ /**
53
+ * Print QR code and manual key to terminal for initial setup
54
+ */
55
+ printSetupInstructions(): void;
56
+ /**
57
+ * Check if 2FA setup is complete
58
+ */
59
+ isSetupComplete(): boolean;
60
+ /**
61
+ * Verify a TOTP code and issue a JWT token
62
+ */
63
+ verifyCode(code: string, trustBrowser?: boolean): VerifyResult;
64
+ /**
65
+ * Issue a new JWT token
66
+ */
67
+ private issueToken;
68
+ /**
69
+ * Validate a JWT token
70
+ * Returns the payload if valid, null if invalid
71
+ */
72
+ validateToken(token: string): JWTPayload | null;
73
+ /**
74
+ * Get auth status for a request
75
+ */
76
+ getAuthStatus(token?: string): AuthStatus;
77
+ /**
78
+ * Get the TOTP secret (for testing only)
79
+ * @internal
80
+ */
81
+ _getTotpSecret(): string | null;
82
+ }
83
+ export declare const authService: AuthService;
84
+ export {};