@ai-ide-bridge/cli 1.0.4 → 1.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 (134) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/commands/daemon.d.ts +1 -0
  3. package/dist/commands/daemon.js +107 -13
  4. package/dist/commands/doctor.js +1 -1
  5. package/dist/commands/init.js +30 -4
  6. package/dist/commands/login.d.ts +1 -0
  7. package/dist/commands/login.js +62 -0
  8. package/dist/commands/logout.d.ts +1 -0
  9. package/dist/commands/logout.js +12 -0
  10. package/dist/commands/start.js +4 -4
  11. package/dist/core/config.d.ts +4 -0
  12. package/dist/core/config.js +43 -0
  13. package/dist/core/daemon-session.d.ts +14 -0
  14. package/dist/core/daemon-session.js +179 -0
  15. package/dist/core/daemon.d.ts +16 -0
  16. package/dist/core/daemon.js +168 -0
  17. package/dist/core/formatter.d.ts +3 -0
  18. package/dist/core/formatter.js +44 -0
  19. package/dist/core/index.d.ts +9 -0
  20. package/dist/core/index.js +9 -0
  21. package/dist/core/parser.d.ts +164 -0
  22. package/dist/core/parser.js +37 -0
  23. package/dist/core/registry.d.ts +16 -0
  24. package/dist/core/registry.js +53 -0
  25. package/dist/core/server.d.ts +19 -0
  26. package/dist/core/server.js +185 -0
  27. package/dist/core/session.d.ts +11 -0
  28. package/dist/core/session.js +39 -0
  29. package/dist/core/types.d.ts +166 -0
  30. package/dist/core/types.js +44 -0
  31. package/dist/index.js +22 -5
  32. package/dist/oauth/device-flow.d.ts +12 -0
  33. package/dist/oauth/device-flow.js +93 -0
  34. package/dist/oauth/flow.d.ts +11 -0
  35. package/dist/oauth/flow.js +75 -0
  36. package/dist/oauth/index.d.ts +6 -0
  37. package/dist/oauth/index.js +5 -0
  38. package/dist/oauth/lifecycle.d.ts +13 -0
  39. package/dist/oauth/lifecycle.js +56 -0
  40. package/dist/oauth/providers.d.ts +2 -0
  41. package/dist/oauth/providers.js +19 -0
  42. package/dist/oauth/storage-file.d.ts +2 -0
  43. package/dist/oauth/storage-file.js +68 -0
  44. package/dist/oauth/storage.d.ts +2 -0
  45. package/dist/oauth/storage.js +4 -0
  46. package/dist/oauth/types.d.ts +44 -0
  47. package/dist/oauth/types.js +1 -0
  48. package/dist/plugins/copilot/auth.d.ts +7 -0
  49. package/dist/plugins/copilot/auth.js +30 -0
  50. package/dist/plugins/copilot/index.d.ts +5 -0
  51. package/dist/plugins/copilot/index.js +4 -0
  52. package/dist/plugins/copilot/plugin.d.ts +8 -0
  53. package/dist/plugins/copilot/plugin.js +29 -0
  54. package/dist/plugins/copilot/session.d.ts +8 -0
  55. package/dist/plugins/copilot/session.js +115 -0
  56. package/dist/plugins/copilot/tools.d.ts +10 -0
  57. package/dist/plugins/copilot/tools.js +10 -0
  58. package/dist/plugins/copilot/types.d.ts +15 -0
  59. package/dist/plugins/copilot/types.js +27 -0
  60. package/dist/plugins/cursor/index.d.ts +2 -0
  61. package/dist/plugins/cursor/index.js +2 -0
  62. package/dist/plugins/cursor/plugin.d.ts +8 -0
  63. package/dist/plugins/cursor/plugin.js +36 -0
  64. package/dist/plugins/cursor/session.d.ts +11 -0
  65. package/dist/plugins/cursor/session.js +69 -0
  66. package/dist/plugins/cursor/tools.d.ts +11 -0
  67. package/dist/plugins/cursor/tools.js +13 -0
  68. package/dist/plugins/windsurf/auth.d.ts +3 -0
  69. package/dist/plugins/windsurf/auth.js +20 -0
  70. package/dist/plugins/windsurf/daemon.d.ts +6 -0
  71. package/dist/plugins/windsurf/daemon.js +16 -0
  72. package/dist/plugins/windsurf/index.d.ts +5 -0
  73. package/dist/plugins/windsurf/index.js +4 -0
  74. package/dist/plugins/windsurf/models.d.ts +2 -0
  75. package/dist/plugins/windsurf/models.js +42 -0
  76. package/dist/plugins/windsurf/plugin.d.ts +8 -0
  77. package/dist/plugins/windsurf/plugin.js +31 -0
  78. package/dist/plugins/windsurf/session.d.ts +5 -0
  79. package/dist/plugins/windsurf/session.js +6 -0
  80. package/dist/plugins/windsurf/tools.d.ts +3 -0
  81. package/dist/plugins/windsurf/tools.js +10 -0
  82. package/dist/plugins/windsurf/types.d.ts +22 -0
  83. package/dist/plugins/windsurf/types.js +1 -0
  84. package/dist/utils/config.d.ts +1 -1
  85. package/dist/utils/config.js +1 -1
  86. package/dist/utils/platform.d.ts +1 -0
  87. package/dist/utils/platform.js +3 -0
  88. package/package.json +3 -5
  89. package/src/commands/daemon.ts +112 -13
  90. package/src/commands/doctor.ts +1 -1
  91. package/src/commands/init.ts +29 -4
  92. package/src/commands/login.ts +98 -0
  93. package/src/commands/logout.ts +15 -0
  94. package/src/commands/start.ts +4 -4
  95. package/src/core/config.ts +45 -0
  96. package/src/core/daemon-session.ts +199 -0
  97. package/src/core/daemon.ts +206 -0
  98. package/src/core/formatter.ts +56 -0
  99. package/src/core/index.ts +9 -0
  100. package/src/core/parser.ts +47 -0
  101. package/src/core/registry.ts +62 -0
  102. package/src/core/server.ts +211 -0
  103. package/src/core/session.ts +54 -0
  104. package/src/core/types.ts +100 -0
  105. package/src/index.ts +22 -4
  106. package/src/oauth/device-flow.ts +111 -0
  107. package/src/oauth/flow.ts +94 -0
  108. package/src/oauth/index.ts +6 -0
  109. package/src/oauth/lifecycle.ts +77 -0
  110. package/src/oauth/providers.ts +21 -0
  111. package/src/oauth/storage-file.ts +77 -0
  112. package/src/oauth/storage.ts +6 -0
  113. package/src/oauth/types.ts +50 -0
  114. package/src/plugins/copilot/auth.ts +39 -0
  115. package/src/plugins/copilot/index.ts +5 -0
  116. package/src/plugins/copilot/plugin.ts +31 -0
  117. package/src/plugins/copilot/session.ts +130 -0
  118. package/src/plugins/copilot/tools.ts +21 -0
  119. package/src/plugins/copilot/types.ts +43 -0
  120. package/src/plugins/cursor/index.ts +2 -0
  121. package/src/plugins/cursor/plugin.ts +37 -0
  122. package/src/plugins/cursor/session.ts +78 -0
  123. package/src/plugins/cursor/tools.ts +25 -0
  124. package/src/plugins/windsurf/auth.ts +23 -0
  125. package/src/plugins/windsurf/daemon.ts +24 -0
  126. package/src/plugins/windsurf/index.ts +5 -0
  127. package/src/plugins/windsurf/models.ts +44 -0
  128. package/src/plugins/windsurf/plugin.ts +34 -0
  129. package/src/plugins/windsurf/session.ts +8 -0
  130. package/src/plugins/windsurf/tools.ts +13 -0
  131. package/src/plugins/windsurf/types.ts +24 -0
  132. package/src/utils/config.ts +1 -1
  133. package/src/utils/platform.ts +3 -0
  134. package/test/daemon.test.ts +224 -0
@@ -0,0 +1,168 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { accessSync, chmodSync, constants, existsSync, mkdirSync, unlinkSync, writeFileSync, } from 'node:fs';
3
+ import { homedir, platform, arch } from 'node:os';
4
+ import { join } from 'node:path';
5
+ import { createHash } from 'node:crypto';
6
+ import { get as httpGet, request as httpRequest } from 'node:http';
7
+ import { request as httpsRequest } from 'node:https';
8
+ import { URL } from 'node:url';
9
+ export function createDaemonManager(config) {
10
+ const defaultDaemonsDir = () => join(homedir(), '.llm-bridge', 'daemons');
11
+ const getDaemonsDir = () => config.daemonsDir ?? defaultDaemonsDir();
12
+ return {
13
+ binaryName: config.binaryName,
14
+ async locate() {
15
+ if (config.envVar && process.env[config.envVar]) {
16
+ const envPath = process.env[config.envVar];
17
+ if (existsSync(envPath)) {
18
+ try {
19
+ accessSync(envPath, constants.X_OK);
20
+ return envPath;
21
+ }
22
+ catch {
23
+ // Not executable, skip
24
+ }
25
+ }
26
+ }
27
+ for (const p of config.knownPaths) {
28
+ if (existsSync(p)) {
29
+ try {
30
+ accessSync(p, constants.X_OK);
31
+ return p;
32
+ }
33
+ catch {
34
+ // Not executable, skip
35
+ }
36
+ }
37
+ }
38
+ const daemonsDir = getDaemonsDir();
39
+ const managedPath = join(daemonsDir, config.binaryName);
40
+ if (existsSync(managedPath)) {
41
+ try {
42
+ accessSync(managedPath, constants.X_OK);
43
+ return managedPath;
44
+ }
45
+ catch {
46
+ // Not executable, skip
47
+ }
48
+ }
49
+ return null;
50
+ },
51
+ async download() {
52
+ const daemonsDir = getDaemonsDir();
53
+ mkdirSync(daemonsDir, { recursive: true });
54
+ const destPath = join(daemonsDir, config.binaryName);
55
+ const url = config.downloadUrl.replace('{platform}', platform()).replace('{arch}', arch());
56
+ const MAX_REDIRECTS = 5;
57
+ const MAX_DOWNLOAD_SIZE = 500 * 1024 * 1024;
58
+ let currentReq = null;
59
+ const followRedirect = (currentUrl, redirectCount) => new Promise((resolve, reject) => {
60
+ const parsed = new URL(currentUrl);
61
+ const requestFn = parsed.protocol === 'https:' ? httpsRequest : httpRequest;
62
+ currentReq = requestFn(currentUrl, (res) => {
63
+ if (res.statusCode === 301 ||
64
+ res.statusCode === 302 ||
65
+ res.statusCode === 307 ||
66
+ res.statusCode === 308) {
67
+ if (redirectCount >= MAX_REDIRECTS) {
68
+ reject(new Error('Too many redirects'));
69
+ return;
70
+ }
71
+ const location = res.headers.location;
72
+ if (location) {
73
+ const redirectUrl = new URL(location, currentUrl);
74
+ if (redirectUrl.protocol !== 'http:' && redirectUrl.protocol !== 'https:') {
75
+ reject(new Error(`Redirect to unsupported protocol: ${redirectUrl.protocol}`));
76
+ res.resume();
77
+ return;
78
+ }
79
+ res.resume();
80
+ followRedirect(redirectUrl.href, redirectCount + 1)
81
+ .then(resolve)
82
+ .catch(reject);
83
+ }
84
+ else {
85
+ reject(new Error('Redirect with no location'));
86
+ }
87
+ return;
88
+ }
89
+ if (res.statusCode !== 200) {
90
+ reject(new Error(`Download failed: ${res.statusCode}`));
91
+ return;
92
+ }
93
+ const chunks = [];
94
+ let totalSize = 0;
95
+ res.on('data', (chunk) => {
96
+ totalSize += chunk.length;
97
+ if (totalSize > MAX_DOWNLOAD_SIZE) {
98
+ reject(new Error(`Download exceeds maximum size (${MAX_DOWNLOAD_SIZE} bytes)`));
99
+ res.destroy();
100
+ return;
101
+ }
102
+ chunks.push(chunk);
103
+ });
104
+ res.on('end', () => {
105
+ resolve(Buffer.concat(chunks));
106
+ });
107
+ });
108
+ currentReq.on('error', reject);
109
+ currentReq.end();
110
+ });
111
+ const downloadPromise = followRedirect(url, 0);
112
+ const timeoutPromise = new Promise((_, reject) => {
113
+ setTimeout(() => {
114
+ currentReq?.destroy();
115
+ reject(new Error('Download timeout (30s)'));
116
+ }, 30000);
117
+ });
118
+ return Promise.race([downloadPromise, timeoutPromise]).then((data) => {
119
+ try {
120
+ writeFileSync(destPath, data);
121
+ const hash = createHash('sha256').update(data).digest('hex');
122
+ if (hash !== config.checksum) {
123
+ throw new Error(`Checksum mismatch: expected ${config.checksum}, got ${hash}`);
124
+ }
125
+ chmodSync(destPath, 0o755);
126
+ return destPath;
127
+ }
128
+ catch (err) {
129
+ try {
130
+ unlinkSync(destPath);
131
+ }
132
+ catch { }
133
+ throw err;
134
+ }
135
+ });
136
+ },
137
+ spawn(binaryPath, args) {
138
+ return spawn(binaryPath, args, {
139
+ stdio: ['pipe', 'pipe', 'pipe'],
140
+ });
141
+ },
142
+ async healthCheck(port) {
143
+ const path = await this.locate();
144
+ if (!path)
145
+ return false;
146
+ if (!existsSync(path))
147
+ return false;
148
+ if (port) {
149
+ try {
150
+ return new Promise((resolve) => {
151
+ const req = httpGet(`http://127.0.0.1:${port}/health`, (res) => {
152
+ resolve(res.statusCode === 200);
153
+ });
154
+ req.on('error', () => resolve(false));
155
+ req.setTimeout(5000, () => {
156
+ req.destroy();
157
+ resolve(false);
158
+ });
159
+ });
160
+ }
161
+ catch {
162
+ return false;
163
+ }
164
+ }
165
+ return true;
166
+ },
167
+ };
168
+ }
@@ -0,0 +1,3 @@
1
+ import type { StreamChunk } from './types.js';
2
+ export declare function formatStreamChunk(chunk: StreamChunk, model: string, completionId: string): string;
3
+ export declare function formatCompletion(content: string, model: string, completionId: string): Record<string, unknown>;
@@ -0,0 +1,44 @@
1
+ export function formatStreamChunk(chunk, model, completionId) {
2
+ const delta = {};
3
+ let finishReason = null;
4
+ if (chunk.type === 'text' && chunk.content) {
5
+ delta.content = chunk.content;
6
+ }
7
+ if (chunk.type === 'tool_call' && chunk.toolCall) {
8
+ delta.tool_calls = [
9
+ {
10
+ index: 0,
11
+ id: chunk.toolCall.id,
12
+ type: 'function',
13
+ function: { name: chunk.toolCall.name, arguments: chunk.toolCall.arguments },
14
+ },
15
+ ];
16
+ }
17
+ if (chunk.finishReason) {
18
+ finishReason = chunk.finishReason;
19
+ }
20
+ const payload = {
21
+ id: completionId,
22
+ object: 'chat.completion.chunk',
23
+ created: Math.floor(Date.now() / 1000),
24
+ model,
25
+ choices: [{ index: 0, delta, finish_reason: finishReason }],
26
+ };
27
+ return `data: ${JSON.stringify(payload)}\n\n`;
28
+ }
29
+ export function formatCompletion(content, model, completionId) {
30
+ return {
31
+ id: completionId,
32
+ object: 'chat.completion',
33
+ created: Math.floor(Date.now() / 1000),
34
+ model,
35
+ choices: [
36
+ {
37
+ index: 0,
38
+ message: { role: 'assistant', content },
39
+ finish_reason: 'stop',
40
+ },
41
+ ],
42
+ usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },
43
+ };
44
+ }
@@ -0,0 +1,9 @@
1
+ export * from './types.js';
2
+ export { BridgeServer } from './server.js';
3
+ export { SessionStore } from './session.js';
4
+ export { parseChatRequest, parseModelsRequest } from './parser.js';
5
+ export { formatStreamChunk, formatCompletion } from './formatter.js';
6
+ export { PluginRegistry } from './registry.js';
7
+ export { loadConfig, saveConfig, configPath } from './config.js';
8
+ export { createDaemonManager, type DaemonManager } from './daemon.js';
9
+ export { DaemonBridgeSession } from './daemon-session.js';
@@ -0,0 +1,9 @@
1
+ export * from './types.js';
2
+ export { BridgeServer } from './server.js';
3
+ export { SessionStore } from './session.js';
4
+ export { parseChatRequest, parseModelsRequest } from './parser.js';
5
+ export { formatStreamChunk, formatCompletion } from './formatter.js';
6
+ export { PluginRegistry } from './registry.js';
7
+ export { loadConfig, saveConfig, configPath } from './config.js';
8
+ export { createDaemonManager } from './daemon.js';
9
+ export { DaemonBridgeSession } from './daemon-session.js';
@@ -0,0 +1,164 @@
1
+ import http from 'node:http';
2
+ import { z } from 'zod';
3
+ declare const ChatRequestSchema: z.ZodObject<{
4
+ model: z.ZodString;
5
+ messages: z.ZodArray<z.ZodObject<{
6
+ role: z.ZodEnum<["system", "user", "assistant", "tool", "function"]>;
7
+ content: z.ZodOptional<z.ZodNullable<z.ZodString>>;
8
+ name: z.ZodOptional<z.ZodString>;
9
+ tool_call_id: z.ZodOptional<z.ZodString>;
10
+ tool_calls: z.ZodOptional<z.ZodArray<z.ZodObject<{
11
+ id: z.ZodString;
12
+ type: z.ZodLiteral<"function">;
13
+ function: z.ZodObject<{
14
+ name: z.ZodString;
15
+ arguments: z.ZodString;
16
+ }, "strip", z.ZodTypeAny, {
17
+ name: string;
18
+ arguments: string;
19
+ }, {
20
+ name: string;
21
+ arguments: string;
22
+ }>;
23
+ }, "strip", z.ZodTypeAny, {
24
+ function: {
25
+ name: string;
26
+ arguments: string;
27
+ };
28
+ type: "function";
29
+ id: string;
30
+ }, {
31
+ function: {
32
+ name: string;
33
+ arguments: string;
34
+ };
35
+ type: "function";
36
+ id: string;
37
+ }>, "many">>;
38
+ }, "strip", z.ZodTypeAny, {
39
+ role: "function" | "system" | "user" | "assistant" | "tool";
40
+ content?: string | null | undefined;
41
+ name?: string | undefined;
42
+ tool_call_id?: string | undefined;
43
+ tool_calls?: {
44
+ function: {
45
+ name: string;
46
+ arguments: string;
47
+ };
48
+ type: "function";
49
+ id: string;
50
+ }[] | undefined;
51
+ }, {
52
+ role: "function" | "system" | "user" | "assistant" | "tool";
53
+ content?: string | null | undefined;
54
+ name?: string | undefined;
55
+ tool_call_id?: string | undefined;
56
+ tool_calls?: {
57
+ function: {
58
+ name: string;
59
+ arguments: string;
60
+ };
61
+ type: "function";
62
+ id: string;
63
+ }[] | undefined;
64
+ }>, "many">;
65
+ stream: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
66
+ tools: z.ZodOptional<z.ZodArray<z.ZodObject<{
67
+ type: z.ZodLiteral<"function">;
68
+ function: z.ZodObject<{
69
+ name: z.ZodString;
70
+ description: z.ZodOptional<z.ZodString>;
71
+ parameters: z.ZodRecord<z.ZodString, z.ZodUnknown>;
72
+ }, "strip", z.ZodTypeAny, {
73
+ name: string;
74
+ parameters: Record<string, unknown>;
75
+ description?: string | undefined;
76
+ }, {
77
+ name: string;
78
+ parameters: Record<string, unknown>;
79
+ description?: string | undefined;
80
+ }>;
81
+ }, "strip", z.ZodTypeAny, {
82
+ function: {
83
+ name: string;
84
+ parameters: Record<string, unknown>;
85
+ description?: string | undefined;
86
+ };
87
+ type: "function";
88
+ }, {
89
+ function: {
90
+ name: string;
91
+ parameters: Record<string, unknown>;
92
+ description?: string | undefined;
93
+ };
94
+ type: "function";
95
+ }>, "many">>;
96
+ tool_choice: z.ZodOptional<z.ZodUnknown>;
97
+ }, "strip", z.ZodTypeAny, {
98
+ model: string;
99
+ messages: {
100
+ role: "function" | "system" | "user" | "assistant" | "tool";
101
+ content?: string | null | undefined;
102
+ name?: string | undefined;
103
+ tool_call_id?: string | undefined;
104
+ tool_calls?: {
105
+ function: {
106
+ name: string;
107
+ arguments: string;
108
+ };
109
+ type: "function";
110
+ id: string;
111
+ }[] | undefined;
112
+ }[];
113
+ stream: boolean;
114
+ tools?: {
115
+ function: {
116
+ name: string;
117
+ parameters: Record<string, unknown>;
118
+ description?: string | undefined;
119
+ };
120
+ type: "function";
121
+ }[] | undefined;
122
+ tool_choice?: unknown;
123
+ }, {
124
+ model: string;
125
+ messages: {
126
+ role: "function" | "system" | "user" | "assistant" | "tool";
127
+ content?: string | null | undefined;
128
+ name?: string | undefined;
129
+ tool_call_id?: string | undefined;
130
+ tool_calls?: {
131
+ function: {
132
+ name: string;
133
+ arguments: string;
134
+ };
135
+ type: "function";
136
+ id: string;
137
+ }[] | undefined;
138
+ }[];
139
+ tools?: {
140
+ function: {
141
+ name: string;
142
+ parameters: Record<string, unknown>;
143
+ description?: string | undefined;
144
+ };
145
+ type: "function";
146
+ }[] | undefined;
147
+ stream?: boolean | undefined;
148
+ tool_choice?: unknown;
149
+ }>;
150
+ export type ParsedChatRequest = z.infer<typeof ChatRequestSchema>;
151
+ export declare function parseChatRequest(req: http.IncomingMessage): Promise<{
152
+ success: true;
153
+ data: ParsedChatRequest;
154
+ } | {
155
+ success: false;
156
+ error: string;
157
+ }>;
158
+ export declare function parseModelsRequest(_req: http.IncomingMessage): {
159
+ success: true;
160
+ } | {
161
+ success: false;
162
+ error: string;
163
+ };
164
+ export {};
@@ -0,0 +1,37 @@
1
+ import { z } from 'zod';
2
+ import { MessageSchema, ToolDefinitionSchema } from './types.js';
3
+ const ChatRequestSchema = z.object({
4
+ model: z.string().min(1, 'model is required'),
5
+ messages: z.array(MessageSchema).min(1, 'messages must have at least one message'),
6
+ stream: z.boolean().optional().default(false),
7
+ tools: z.array(ToolDefinitionSchema).optional(),
8
+ tool_choice: z.unknown().optional(),
9
+ });
10
+ export async function parseChatRequest(req) {
11
+ try {
12
+ const body = await readBody(req);
13
+ const json = JSON.parse(body);
14
+ const result = ChatRequestSchema.safeParse(json);
15
+ if (!result.success) {
16
+ return { success: false, error: result.error.errors[0].message };
17
+ }
18
+ return { success: true, data: result.data };
19
+ }
20
+ catch (e) {
21
+ if (e instanceof SyntaxError) {
22
+ return { success: false, error: 'Invalid JSON body' };
23
+ }
24
+ return { success: false, error: e instanceof Error ? e.message : 'Unknown error' };
25
+ }
26
+ }
27
+ export function parseModelsRequest(_req) {
28
+ return { success: true };
29
+ }
30
+ function readBody(req) {
31
+ return new Promise((resolve, reject) => {
32
+ const chunks = [];
33
+ req.on('data', (c) => chunks.push(c));
34
+ req.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
35
+ req.on('error', reject);
36
+ });
37
+ }
@@ -0,0 +1,16 @@
1
+ import type { BridgePlugin, PluginHealth } from './types.js';
2
+ export declare class PluginRegistry {
3
+ private plugins;
4
+ private activePluginName;
5
+ private defaultPluginName;
6
+ private health;
7
+ register(plugin: BridgePlugin): void;
8
+ getPlugin(name: string): BridgePlugin | undefined;
9
+ setActive(name: string): void;
10
+ setDefault(name: string): void;
11
+ getActivePlugin(): BridgePlugin | null;
12
+ getDefaultPlugin(): BridgePlugin | null;
13
+ listPlugins(): BridgePlugin[];
14
+ markUnhealthy(name: string, error: string): void;
15
+ getHealth(name: string): PluginHealth | undefined;
16
+ }
@@ -0,0 +1,53 @@
1
+ export class PluginRegistry {
2
+ plugins = new Map();
3
+ activePluginName = null;
4
+ defaultPluginName = null;
5
+ health = new Map();
6
+ register(plugin) {
7
+ this.plugins.set(plugin.name, plugin);
8
+ this.health.set(plugin.name, {
9
+ name: plugin.name,
10
+ healthy: true,
11
+ lastChecked: new Date(),
12
+ });
13
+ }
14
+ getPlugin(name) {
15
+ return this.plugins.get(name);
16
+ }
17
+ setActive(name) {
18
+ if (!this.plugins.has(name)) {
19
+ throw new Error(`Plugin "${name}" is not registered`);
20
+ }
21
+ this.activePluginName = name;
22
+ }
23
+ setDefault(name) {
24
+ if (!this.plugins.has(name)) {
25
+ throw new Error(`Plugin "${name}" is not registered`);
26
+ }
27
+ this.defaultPluginName = name;
28
+ }
29
+ getActivePlugin() {
30
+ if (!this.activePluginName)
31
+ return null;
32
+ return this.plugins.get(this.activePluginName) ?? null;
33
+ }
34
+ getDefaultPlugin() {
35
+ if (!this.defaultPluginName)
36
+ return null;
37
+ return this.plugins.get(this.defaultPluginName) ?? null;
38
+ }
39
+ listPlugins() {
40
+ return Array.from(this.plugins.values());
41
+ }
42
+ markUnhealthy(name, error) {
43
+ this.health.set(name, {
44
+ name,
45
+ healthy: false,
46
+ lastChecked: new Date(),
47
+ error,
48
+ });
49
+ }
50
+ getHealth(name) {
51
+ return this.health.get(name);
52
+ }
53
+ }
@@ -0,0 +1,19 @@
1
+ import { BridgeConfig, BridgePlugin } from './types.js';
2
+ export declare class BridgeServer {
3
+ private server;
4
+ private registry;
5
+ private sessions;
6
+ private config;
7
+ constructor(config?: Partial<BridgeConfig>);
8
+ start(): Promise<void>;
9
+ stop(): Promise<void>;
10
+ address(): import('net').AddressInfo | string | null;
11
+ private handleRequest;
12
+ private handleModels;
13
+ private resolvePlugin;
14
+ private handleChatCompletions;
15
+ private jsonResponse;
16
+ registerPlugin(plugin: BridgePlugin): void;
17
+ setActivePlugin(name: string): void;
18
+ setDefaultPlugin(name: string): void;
19
+ }