@kraki/head 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.
package/dist/cli.d.ts ADDED
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @kraki/head — CLI entry point for the Kraki relay server.
4
+ *
5
+ * Usage:
6
+ * npx @kraki/head
7
+ * npx @kraki/head --port 8080
8
+ * npx @kraki/head --auth github --e2e true
9
+ *
10
+ * Environment variables:
11
+ * PORT Server port (default: 4000)
12
+ * DB_PATH SQLite database path (default: kraki-head.db)
13
+ * AUTH_MODE Auth mode: open | github | apikey (default: open)
14
+ * E2E_MODE Enable E2E encryption: true | false (default: false)
15
+ * API_KEY API key for apikey auth mode
16
+ * GITHUB_CLIENT_ID GitHub OAuth App client ID (for web login)
17
+ * GITHUB_CLIENT_SECRET GitHub OAuth App client secret (for web login)
18
+ * LOG_LEVEL Log level: debug | info | warn | error (default: info)
19
+ * LOG_PATH Log file path (optional)
20
+ */
21
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,156 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @kraki/head — CLI entry point for the Kraki relay server.
4
+ *
5
+ * Usage:
6
+ * npx @kraki/head
7
+ * npx @kraki/head --port 8080
8
+ * npx @kraki/head --auth github --e2e true
9
+ *
10
+ * Environment variables:
11
+ * PORT Server port (default: 4000)
12
+ * DB_PATH SQLite database path (default: kraki-head.db)
13
+ * AUTH_MODE Auth mode: open | github | apikey (default: open)
14
+ * E2E_MODE Enable E2E encryption: true | false (default: false)
15
+ * API_KEY API key for apikey auth mode
16
+ * GITHUB_CLIENT_ID GitHub OAuth App client ID (for web login)
17
+ * GITHUB_CLIENT_SECRET GitHub OAuth App client secret (for web login)
18
+ * LOG_LEVEL Log level: debug | info | warn | error (default: info)
19
+ * LOG_PATH Log file path (optional)
20
+ */
21
+ import { config as dotenvConfig } from 'dotenv';
22
+ import { resolve, dirname } from 'path';
23
+ import { fileURLToPath } from 'url';
24
+ // Load .env from the head package directory (works regardless of cwd)
25
+ const __dirname = dirname(fileURLToPath(import.meta.url));
26
+ dotenvConfig({ path: resolve(__dirname, '..', '.env') });
27
+ import { createServer } from 'http';
28
+ import { Storage } from './storage.js';
29
+ import { ChannelManager } from './channel-manager.js';
30
+ import { Router } from './router.js';
31
+ import { HeadServer } from './server.js';
32
+ import { GitHubAuthProvider, OpenAuthProvider, ApiKeyAuthProvider, ThrottledAuthProvider } from './auth.js';
33
+ import { Logger, setGlobalLogger } from './logger.js';
34
+ const VERSION = '0.1.0';
35
+ // --- CLI flags ---
36
+ const args = process.argv.slice(2);
37
+ if (args.includes('--help') || args.includes('-h')) {
38
+ console.log(`
39
+ 🦑 @kraki/head v${VERSION}
40
+
41
+ Usage: kraki-relay [options]
42
+
43
+ Options:
44
+ --port <n> Server port (default: 4000, env: PORT)
45
+ --db <path> SQLite database path (default: kraki-head.db, env: DB_PATH)
46
+ --auth <mode> Auth mode: open | github | apikey (default: open, env: AUTH_MODE)
47
+ --e2e <bool> Enable E2E encryption (default: false, env: E2E_MODE)
48
+ --log <level> Log level: debug | info | warn | error (default: info)
49
+ --help, -h Show this help
50
+ --version, -v Show version
51
+
52
+ GitHub OAuth (env only, for web login):
53
+ GITHUB_CLIENT_ID GitHub OAuth App client ID
54
+ GITHUB_CLIENT_SECRET GitHub OAuth App client secret
55
+ `);
56
+ process.exit(0);
57
+ }
58
+ if (args.includes('--version') || args.includes('-v')) {
59
+ console.log(VERSION);
60
+ process.exit(0);
61
+ }
62
+ function flag(name, fallback) {
63
+ const i = args.indexOf(`--${name}`);
64
+ if (i === -1)
65
+ return fallback;
66
+ return args[i + 1] || fallback;
67
+ }
68
+ const PORT = parseInt(flag('port', process.env.PORT || '4000'), 10);
69
+ const DB_PATH = flag('db', process.env.DB_PATH || 'kraki-head.db');
70
+ const AUTH_MODES = flag('auth', process.env.AUTH_MODE || 'open').split(',').map(s => s.trim());
71
+ const API_KEY = process.env.API_KEY;
72
+ const GITHUB_CLIENT_ID = process.env.GITHUB_CLIENT_ID;
73
+ const GITHUB_CLIENT_SECRET = process.env.GITHUB_CLIENT_SECRET;
74
+ const E2E = flag('e2e', process.env.E2E_MODE || 'false') === 'true';
75
+ const PAIRING = process.env.PAIRING_ENABLED !== 'false'; // default true
76
+ const LOG_LEVEL = flag('log', process.env.LOG_LEVEL || 'info');
77
+ const LOG_PATH = process.env.LOG_PATH;
78
+ function createAuthProviders() {
79
+ const providers = new Map();
80
+ for (const mode of AUTH_MODES) {
81
+ switch (mode) {
82
+ case 'github': {
83
+ if (!GITHUB_CLIENT_ID || !GITHUB_CLIENT_SECRET) {
84
+ console.warn(' ⚠ GitHub OAuth: GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRET not set.');
85
+ console.warn(' Web "Sign in with GitHub" will be disabled. Add them to .env or environment.');
86
+ console.warn(' Token auth (gh auth token) and QR pairing still work.');
87
+ }
88
+ const ghProvider = new GitHubAuthProvider({
89
+ clientId: GITHUB_CLIENT_ID,
90
+ clientSecret: GITHUB_CLIENT_SECRET,
91
+ });
92
+ providers.set('github', new ThrottledAuthProvider(ghProvider));
93
+ if (GITHUB_CLIENT_ID && GITHUB_CLIENT_SECRET) {
94
+ console.log(' GitHub OAuth configured (web login enabled)');
95
+ }
96
+ break;
97
+ }
98
+ case 'apikey':
99
+ if (!API_KEY) {
100
+ console.error('Error: API_KEY environment variable required for apikey auth mode');
101
+ process.exit(1);
102
+ }
103
+ providers.set('apikey', new ThrottledAuthProvider(new ApiKeyAuthProvider(API_KEY)));
104
+ break;
105
+ case 'open':
106
+ providers.set('open', new OpenAuthProvider());
107
+ break;
108
+ default:
109
+ console.error(`Error: Unknown auth mode '${mode}'. Use: github, apikey, open`);
110
+ process.exit(1);
111
+ }
112
+ }
113
+ return providers;
114
+ }
115
+ const logger = new Logger({
116
+ level: LOG_LEVEL,
117
+ filePath: LOG_PATH,
118
+ stdout: true,
119
+ });
120
+ setGlobalLogger(logger);
121
+ logger.info('Kraki Head starting...');
122
+ const authProviders = createAuthProviders();
123
+ const storage = new Storage(DB_PATH);
124
+ const cm = new ChannelManager(storage);
125
+ const router = new Router(cm);
126
+ const head = new HeadServer(cm, router, {
127
+ authProviders,
128
+ authModes: AUTH_MODES,
129
+ e2e: E2E,
130
+ pairingEnabled: PAIRING,
131
+ });
132
+ const httpServer = createServer((req, res) => {
133
+ res.writeHead(200, { 'Content-Type': 'application/json' });
134
+ res.end(JSON.stringify({ name: '@kraki/head', version: VERSION, status: 'ok' }));
135
+ });
136
+ head.attach(httpServer);
137
+ httpServer.listen(PORT, () => {
138
+ logger.info(`Kraki Head listening on port ${PORT}`, {
139
+ ws: `ws://localhost:${PORT}`,
140
+ auth: AUTH_MODES.join(', '),
141
+ e2e: E2E,
142
+ pairing: PAIRING,
143
+ db: DB_PATH,
144
+ });
145
+ });
146
+ async function shutdown() {
147
+ logger.info('Shutting down...');
148
+ head.close();
149
+ storage.close();
150
+ logger.close();
151
+ httpServer.close();
152
+ process.exit(0);
153
+ }
154
+ process.on('SIGINT', shutdown);
155
+ process.on('SIGTERM', shutdown);
156
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export { Storage } from './storage.js';
2
+ export { ChannelManager } from './channel-manager.js';
3
+ export { Router } from './router.js';
4
+ export { HeadServer } from './server.js';
5
+ export type { HeadServerOptions } from './server.js';
6
+ export { GitHubAuthProvider, OpenAuthProvider, ApiKeyAuthProvider, ThrottledAuthProvider } from './auth.js';
7
+ export type { AuthProvider, AuthUser, AuthOutcome, AuthCredentials } from './auth.js';
8
+ export { Logger, getLogger, setGlobalLogger } from './logger.js';
9
+ export type { LoggerOptions, LogLevel } from './logger.js';
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ export { Storage } from './storage.js';
2
+ export { ChannelManager } from './channel-manager.js';
3
+ export { Router } from './router.js';
4
+ export { HeadServer } from './server.js';
5
+ export { GitHubAuthProvider, OpenAuthProvider, ApiKeyAuthProvider, ThrottledAuthProvider } from './auth.js';
6
+ export { Logger, getLogger, setGlobalLogger } from './logger.js';
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAE5G,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,31 @@
1
+ export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
2
+ export interface LoggerOptions {
3
+ /** Minimum log level to output. Default: 'info' */
4
+ level?: LogLevel;
5
+ /** Log file path. If not set, logs to stdout only. */
6
+ filePath?: string;
7
+ /** Max file size in bytes before rotation. Default: 10MB */
8
+ maxFileSize?: number;
9
+ /** Max number of rotated files to keep. Default: 5 */
10
+ maxFiles?: number;
11
+ /** Also log to stdout. Default: true */
12
+ stdout?: boolean;
13
+ }
14
+ export declare class Logger {
15
+ private level;
16
+ private filePath?;
17
+ private maxFileSize;
18
+ private maxFiles;
19
+ private stdout;
20
+ private currentSize;
21
+ constructor(options?: LoggerOptions);
22
+ debug(message: string, data?: Record<string, unknown>): void;
23
+ info(message: string, data?: Record<string, unknown>): void;
24
+ warn(message: string, data?: Record<string, unknown>): void;
25
+ error(message: string, data?: Record<string, unknown>): void;
26
+ close(): void;
27
+ private log;
28
+ private rotate;
29
+ }
30
+ export declare function setGlobalLogger(logger: Logger): void;
31
+ export declare function getLogger(): Logger;
package/dist/logger.js ADDED
@@ -0,0 +1,103 @@
1
+ import { appendFileSync, mkdirSync, existsSync, renameSync, statSync } from 'fs';
2
+ import { dirname } from 'path';
3
+ const LEVEL_ORDER = {
4
+ debug: 0,
5
+ info: 1,
6
+ warn: 2,
7
+ error: 3,
8
+ };
9
+ export class Logger {
10
+ level;
11
+ filePath;
12
+ maxFileSize;
13
+ maxFiles;
14
+ stdout;
15
+ currentSize = 0;
16
+ constructor(options = {}) {
17
+ this.level = LEVEL_ORDER[options.level ?? 'info'];
18
+ this.filePath = options.filePath;
19
+ this.maxFileSize = options.maxFileSize ?? 10 * 1024 * 1024; // 10MB
20
+ this.maxFiles = options.maxFiles ?? 5;
21
+ this.stdout = options.stdout ?? true;
22
+ if (this.filePath) {
23
+ const dir = dirname(this.filePath);
24
+ if (!existsSync(dir))
25
+ mkdirSync(dir, { recursive: true });
26
+ try {
27
+ if (existsSync(this.filePath)) {
28
+ this.currentSize = statSync(this.filePath).size;
29
+ }
30
+ }
31
+ catch {
32
+ this.currentSize = 0;
33
+ }
34
+ }
35
+ }
36
+ debug(message, data) {
37
+ this.log('debug', message, data);
38
+ }
39
+ info(message, data) {
40
+ this.log('info', message, data);
41
+ }
42
+ warn(message, data) {
43
+ this.log('warn', message, data);
44
+ }
45
+ error(message, data) {
46
+ this.log('error', message, data);
47
+ }
48
+ close() {
49
+ // No-op for sync writes, but keeps the interface consistent
50
+ }
51
+ log(level, message, data) {
52
+ if (LEVEL_ORDER[level] < this.level)
53
+ return;
54
+ const entry = {
55
+ time: new Date().toISOString(),
56
+ level,
57
+ msg: message,
58
+ ...data,
59
+ };
60
+ const line = JSON.stringify(entry) + '\n';
61
+ if (this.stdout) {
62
+ const prefix = level === 'error' ? '❌' : level === 'warn' ? '⚠️' : level === 'debug' ? '🔍' : '◈';
63
+ process.stdout.write(`${prefix} [${entry.time}] ${message}${data ? ' ' + JSON.stringify(data) : ''}\n`);
64
+ }
65
+ if (this.filePath) {
66
+ appendFileSync(this.filePath, line);
67
+ this.currentSize += Buffer.byteLength(line);
68
+ if (this.currentSize >= this.maxFileSize) {
69
+ this.rotate();
70
+ }
71
+ }
72
+ }
73
+ rotate() {
74
+ if (!this.filePath)
75
+ return;
76
+ // Shift existing rotated files: .4 → .5, .3 → .4, etc.
77
+ for (let i = this.maxFiles - 1; i >= 1; i--) {
78
+ const from = `${this.filePath}.${i}`;
79
+ const to = `${this.filePath}.${i + 1}`;
80
+ if (existsSync(from)) {
81
+ try {
82
+ renameSync(from, to);
83
+ }
84
+ catch { /* best effort */ }
85
+ }
86
+ }
87
+ // Current → .1
88
+ try {
89
+ renameSync(this.filePath, `${this.filePath}.1`);
90
+ }
91
+ catch { /* best effort */ }
92
+ this.currentSize = 0;
93
+ }
94
+ }
95
+ /** Global logger instance — set via createLogger() in index.ts */
96
+ let globalLogger = new Logger();
97
+ export function setGlobalLogger(logger) {
98
+ globalLogger = logger;
99
+ }
100
+ export function getLogger() {
101
+ return globalLogger;
102
+ }
103
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC;AACjF,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAI/B,MAAM,WAAW,GAA6B;IAC5C,KAAK,EAAE,CAAC;IACR,IAAI,EAAE,CAAC;IACP,IAAI,EAAE,CAAC;IACP,KAAK,EAAE,CAAC;CACT,CAAC;AAeF,MAAM,OAAO,MAAM;IACT,KAAK,CAAS;IACd,QAAQ,CAAU;IAClB,WAAW,CAAS;IACpB,QAAQ,CAAS;IACjB,MAAM,CAAU;IAChB,WAAW,GAAG,CAAC,CAAC;IAExB,YAAY,UAAyB,EAAE;QACrC,IAAI,CAAC,KAAK,GAAG,WAAW,CAAC,OAAO,CAAC,KAAK,IAAI,MAAM,CAAC,CAAC;QAClD,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;QACjC,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO;QACnE,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,CAAC,CAAC;QACtC,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,IAAI,CAAC;QAErC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACnC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;gBAAE,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC1D,IAAI,CAAC;gBACH,IAAI,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;oBAC9B,IAAI,CAAC,WAAW,GAAG,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC;gBAClD,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC;YACvB,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,IAA8B;QACnD,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IACnC,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,IAA8B;QAClD,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IAClC,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,IAA8B;QAClD,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IAClC,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,IAA8B;QACnD,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IACnC,CAAC;IAED,KAAK;QACH,4DAA4D;IAC9D,CAAC;IAEO,GAAG,CAAC,KAAe,EAAE,OAAe,EAAE,IAA8B;QAC1E,IAAI,WAAW,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,KAAK;YAAE,OAAO;QAE5C,MAAM,KAAK,GAAG;YACZ,IAAI,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YAC9B,KAAK;YACL,GAAG,EAAE,OAAO;YACZ,GAAG,IAAI;SACR,CAAC;QAEF,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;QAE1C,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,MAAM,MAAM,GAAG,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC;YAClG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,MAAM,KAAK,KAAK,CAAC,IAAI,KAAK,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;QAC1G,CAAC;QAED,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,cAAc,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;YACpC,IAAI,CAAC,WAAW,IAAI,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;YAC5C,IAAI,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;gBACzC,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,CAAC;QACH,CAAC;IACH,CAAC;IAEO,MAAM;QACZ,IAAI,CAAC,IAAI,CAAC,QAAQ;YAAE,OAAO;QAE3B,uDAAuD;QACvD,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,QAAQ,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAC5C,MAAM,IAAI,GAAG,GAAG,IAAI,CAAC,QAAQ,IAAI,CAAC,EAAE,CAAC;YACrC,MAAM,EAAE,GAAG,GAAG,IAAI,CAAC,QAAQ,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YACvC,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;gBACrB,IAAI,CAAC;oBAAC,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;gBAAC,CAAC;gBAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;YAC3D,CAAC;QACH,CAAC;QAED,eAAe;QACf,IAAI,CAAC;YAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;QACpF,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC;IACvB,CAAC;CACF;AAED,kEAAkE;AAClE,IAAI,YAAY,GAAW,IAAI,MAAM,EAAE,CAAC;AAExC,MAAM,UAAU,eAAe,CAAC,MAAc;IAC5C,YAAY,GAAG,MAAM,CAAC;AACxB,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,OAAO,YAAY,CAAC;AACtB,CAAC"}
@@ -0,0 +1,30 @@
1
+ import type { ProducerMessage, ConsumerMessage, HeadNotice, EncryptedMessage } from '@kraki/protocol';
2
+ import { ChannelManager } from './channel-manager.js';
3
+ type IncomingMessage = ProducerMessage | ConsumerMessage | EncryptedMessage;
4
+ export declare class Router {
5
+ private cm;
6
+ constructor(cm: ChannelManager);
7
+ /**
8
+ * Handle an incoming message from a connected device.
9
+ * Assigns seq, stores if needed, routes to correct recipients.
10
+ */
11
+ handleMessage(fromDeviceId: string, raw: IncomingMessage): void;
12
+ /**
13
+ * Replay stored messages to a device after a given seq.
14
+ * Filters out encrypted messages the device cannot decrypt.
15
+ */
16
+ replay(deviceId: string, afterSeq: number, sessionId?: string): void;
17
+ /**
18
+ * Send a HeadNotice to all connected devices on a channel.
19
+ */
20
+ broadcastNotice(channelId: string, notice: HeadNotice): void;
21
+ private broadcastToTentacles;
22
+ private sendToApps;
23
+ private sendToSessionOwner;
24
+ /**
25
+ * Route create_session to a specific tentacle.
26
+ * Works for both plaintext and encrypted (targetDeviceId from envelope).
27
+ */
28
+ private sendToTentacle;
29
+ }
30
+ export {};
package/dist/router.js ADDED
@@ -0,0 +1,217 @@
1
+ import { shouldStore } from './storage.js';
2
+ import { getLogger } from './logger.js';
3
+ /** Normalize SQLite datetime ("2024-01-15 20:00:00") to ISO 8601 ("2024-01-15T20:00:00.000Z") */
4
+ function toISO(sqliteTimestamp) {
5
+ if (sqliteTimestamp.includes('T'))
6
+ return sqliteTimestamp; // already ISO
7
+ return sqliteTimestamp.replace(' ', 'T') + '.000Z';
8
+ }
9
+ export class Router {
10
+ cm;
11
+ constructor(cm) {
12
+ this.cm = cm;
13
+ }
14
+ /**
15
+ * Handle an incoming message from a connected device.
16
+ * Assigns seq, stores if needed, routes to correct recipients.
17
+ */
18
+ handleMessage(fromDeviceId, raw) {
19
+ const device = this.cm.getConnection(fromDeviceId);
20
+ if (!device)
21
+ return;
22
+ const channelId = device.channelId;
23
+ const seq = this.cm.nextSeq(channelId);
24
+ const logger = getLogger();
25
+ // Track session ownership on session_created (narrow on raw, not stamped)
26
+ if (raw.type === 'session_created' && raw.sessionId) {
27
+ const payload = raw.payload;
28
+ this.cm.registerSession(raw.sessionId, fromDeviceId, {
29
+ agent: payload.agent,
30
+ model: payload.model,
31
+ });
32
+ logger.debug('Session registered', { sessionId: raw.sessionId, device: fromDeviceId, agent: payload.agent });
33
+ }
34
+ // In E2E mode, encrypted messages from tentacles may contain session_created.
35
+ // The tentacle exposes agent/model in the outer envelope for session registration.
36
+ if (device.role === 'tentacle' && raw.sessionId && !this.cm.getSessionOwner(raw.sessionId)) {
37
+ const enc = raw.type === 'encrypted' ? raw : null;
38
+ this.cm.registerSession(raw.sessionId, fromDeviceId, {
39
+ agent: enc?.agent ?? raw.payload?.agent ?? 'unknown',
40
+ model: enc?.model ?? raw.payload?.model,
41
+ });
42
+ logger.debug('Session registered (from envelope)', { sessionId: raw.sessionId, device: fromDeviceId });
43
+ }
44
+ // Stamp envelope fields
45
+ const stamped = {
46
+ ...raw,
47
+ channel: channelId,
48
+ deviceId: fromDeviceId,
49
+ seq,
50
+ timestamp: new Date().toISOString(),
51
+ };
52
+ // Store if this is a storable message type (skip ephemeral messages like deltas)
53
+ const isEphemeral = stamped.type === 'encrypted' && stamped.ephemeral;
54
+ if (shouldStore(stamped.type) && !isEphemeral) {
55
+ // For encrypted messages, store the full envelope (head can't read the inner payload)
56
+ let payloadToStore;
57
+ if (stamped.type === 'encrypted') {
58
+ const enc = stamped;
59
+ payloadToStore = JSON.stringify({ iv: enc.iv, ciphertext: enc.ciphertext, tag: enc.tag, keys: enc.keys });
60
+ }
61
+ else {
62
+ payloadToStore = JSON.stringify(stamped.payload);
63
+ }
64
+ this.cm.getStorage().storeMessage({
65
+ channelId,
66
+ deviceId: fromDeviceId,
67
+ sessionId: stamped.sessionId ?? null,
68
+ seq,
69
+ type: stamped.type,
70
+ payload: payloadToStore,
71
+ });
72
+ }
73
+ const serialized = JSON.stringify(stamped);
74
+ // Route based on sender role
75
+ if (device.role === 'tentacle') {
76
+ this.sendToApps(channelId, serialized);
77
+ logger.debug('Routed tentacle→apps', { type: stamped.type, seq, channel: channelId });
78
+ }
79
+ else if (device.role === 'app') {
80
+ // Encrypted create_session: targetDeviceId in outer envelope
81
+ const targetDeviceId = raw.type === 'create_session'
82
+ ? raw.payload?.targetDeviceId
83
+ : raw.targetDeviceId;
84
+ if (targetDeviceId) {
85
+ const requestId = raw.type === 'create_session'
86
+ ? raw.payload?.requestId
87
+ : undefined;
88
+ this.sendToTentacle(channelId, targetDeviceId, serialized, logger, fromDeviceId, requestId);
89
+ }
90
+ else {
91
+ this.sendToSessionOwner(stamped.sessionId, serialized);
92
+ logger.debug('Routed app→tentacle', { type: stamped.type, seq, sessionId: stamped.sessionId });
93
+ }
94
+ }
95
+ }
96
+ /**
97
+ * Replay stored messages to a device after a given seq.
98
+ * Filters out encrypted messages the device cannot decrypt.
99
+ */
100
+ replay(deviceId, afterSeq, sessionId) {
101
+ const device = this.cm.getConnection(deviceId);
102
+ if (!device)
103
+ return;
104
+ const logger = getLogger();
105
+ const storage = this.cm.getStorage();
106
+ const messages = storage.getMessagesForDevice(device.channelId, afterSeq, deviceId, sessionId);
107
+ let sent = 0;
108
+ let skipped = 0;
109
+ for (const msg of messages) {
110
+ try {
111
+ let envelope;
112
+ if (msg.type === 'encrypted') {
113
+ // Encrypted messages: reconstruct original wire format
114
+ const stored = JSON.parse(msg.payload);
115
+ envelope = {
116
+ channel: msg.channelId,
117
+ deviceId: msg.deviceId,
118
+ seq: msg.seq,
119
+ timestamp: toISO(msg.createdAt),
120
+ type: 'encrypted',
121
+ sessionId: msg.sessionId,
122
+ iv: stored.iv,
123
+ ciphertext: stored.ciphertext,
124
+ tag: stored.tag,
125
+ keys: stored.keys,
126
+ };
127
+ }
128
+ else {
129
+ // Normal messages: reconstruct envelope with parsed payload
130
+ envelope = {
131
+ channel: msg.channelId,
132
+ deviceId: msg.deviceId,
133
+ seq: msg.seq,
134
+ timestamp: toISO(msg.createdAt),
135
+ type: msg.type,
136
+ sessionId: msg.sessionId,
137
+ payload: JSON.parse(msg.payload),
138
+ };
139
+ }
140
+ device.send(JSON.stringify(envelope));
141
+ sent++;
142
+ }
143
+ catch {
144
+ logger.warn('Skipped corrupt message during replay', {
145
+ messageId: msg.id,
146
+ seq: msg.seq,
147
+ type: msg.type,
148
+ });
149
+ skipped++;
150
+ }
151
+ }
152
+ // Send replay_complete with the channel's max seq so the client
153
+ // advances past skipped messages and doesn't re-request them.
154
+ const lastSeq = storage.getMaxSeq(device.channelId);
155
+ device.send(JSON.stringify({ type: 'replay_complete', lastSeq }));
156
+ logger.debug('Replay complete', { deviceId, afterSeq, sent, skipped, lastSeq });
157
+ }
158
+ /**
159
+ * Send a HeadNotice to all connected devices on a channel.
160
+ */
161
+ broadcastNotice(channelId, notice) {
162
+ const serialized = JSON.stringify(notice);
163
+ for (const device of this.cm.getConnectedDevices(channelId)) {
164
+ device.send(serialized);
165
+ }
166
+ }
167
+ broadcastToTentacles(channelId, notice) {
168
+ const serialized = JSON.stringify(notice);
169
+ for (const tentacle of this.cm.getConnectedByRole(channelId, 'tentacle')) {
170
+ tentacle.send(serialized);
171
+ }
172
+ }
173
+ sendToApps(channelId, data) {
174
+ for (const app of this.cm.getConnectedByRole(channelId, 'app')) {
175
+ app.send(data);
176
+ }
177
+ }
178
+ sendToSessionOwner(sessionId, data) {
179
+ if (!sessionId)
180
+ return;
181
+ const ownerDeviceId = this.cm.getSessionOwner(sessionId);
182
+ if (!ownerDeviceId)
183
+ return;
184
+ const device = this.cm.getConnection(ownerDeviceId);
185
+ if (device)
186
+ device.send(data);
187
+ }
188
+ /**
189
+ * Route create_session to a specific tentacle.
190
+ * Works for both plaintext and encrypted (targetDeviceId from envelope).
191
+ */
192
+ sendToTentacle(channelId, targetDeviceId, serialized, logger, fromDeviceId, requestId) {
193
+ const device = this.cm.getConnection(targetDeviceId);
194
+ // Verify target is a tentacle in the SAME channel
195
+ if (device && device.role === 'tentacle' && device.channelId === channelId) {
196
+ device.send(serialized);
197
+ logger.debug('Routed create_session→tentacle', { targetDeviceId });
198
+ }
199
+ else {
200
+ const reason = device && device.channelId !== channelId
201
+ ? `Target device "${targetDeviceId}" belongs to a different channel`
202
+ : `Target device "${targetDeviceId}" is not online`;
203
+ logger.warn('create_session rejected', { targetDeviceId, reason });
204
+ if (fromDeviceId) {
205
+ const sender = this.cm.getConnection(fromDeviceId);
206
+ if (sender) {
207
+ sender.send(JSON.stringify({
208
+ type: 'server_error',
209
+ message: reason,
210
+ requestId,
211
+ }));
212
+ }
213
+ }
214
+ }
215
+ }
216
+ }
217
+ //# sourceMappingURL=router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.js","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"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"}