@byoky/bridge 0.2.0 → 0.4.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/LICENSE +21 -0
- package/bin/byoky-bridge.js +6 -1
- package/dist/host.js +199 -231
- package/dist/installer.d.ts +1 -1
- package/dist/installer.js +22 -7
- package/package.json +6 -6
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 byoky contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/bin/byoky-bridge.js
CHANGED
|
@@ -8,7 +8,12 @@ if (command === 'install' || command === 'uninstall' || command === 'status') {
|
|
|
8
8
|
|
|
9
9
|
console.log('Byoky Bridge\n');
|
|
10
10
|
|
|
11
|
-
if (command === 'install')
|
|
11
|
+
if (command === 'install') {
|
|
12
|
+
// --extension-id <id> for custom/unpacked extension IDs
|
|
13
|
+
const idIdx = process.argv.indexOf('--extension-id');
|
|
14
|
+
const extensionId = idIdx !== -1 ? process.argv[idIdx + 1] : undefined;
|
|
15
|
+
install(extensionId);
|
|
16
|
+
}
|
|
12
17
|
else if (command === 'uninstall') uninstall();
|
|
13
18
|
else if (command === 'status') status();
|
|
14
19
|
} else if (!command || command === 'host') {
|
package/dist/host.js
CHANGED
|
@@ -1,231 +1,145 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
function formatMessagesAsPrompt(request) {
|
|
25
|
-
const parts = [];
|
|
26
|
-
if (request.system) {
|
|
27
|
-
parts.push(`[System: ${request.system}]`);
|
|
28
|
-
}
|
|
29
|
-
for (const msg of request.messages) {
|
|
30
|
-
if (msg.role === "user") {
|
|
31
|
-
parts.push(msg.content);
|
|
32
|
-
} else if (msg.role === "assistant") {
|
|
33
|
-
parts.push(`[Previous assistant response: ${msg.content}]`);
|
|
1
|
+
// src/proxy-server.ts
|
|
2
|
+
import { createServer } from "http";
|
|
3
|
+
var MAX_PENDING_REQUESTS = 100;
|
|
4
|
+
var pendingRequests = /* @__PURE__ */ new Map();
|
|
5
|
+
function handleProxyResponse(msg) {
|
|
6
|
+
const pending = pendingRequests.get(msg.requestId);
|
|
7
|
+
if (!pending) return;
|
|
8
|
+
if (msg.type === "proxy_http_response_meta") {
|
|
9
|
+
const headers = { ...msg.headers };
|
|
10
|
+
delete headers["transfer-encoding"];
|
|
11
|
+
delete headers["content-encoding"];
|
|
12
|
+
delete headers["content-length"];
|
|
13
|
+
pending.res.writeHead(msg.status, headers);
|
|
14
|
+
} else if (msg.type === "proxy_http_response_chunk") {
|
|
15
|
+
pending.res.write(msg.chunk);
|
|
16
|
+
} else if (msg.type === "proxy_http_response_done") {
|
|
17
|
+
pending.res.end();
|
|
18
|
+
clearTimeout(pending.timeout);
|
|
19
|
+
pendingRequests.delete(msg.requestId);
|
|
20
|
+
} else if (msg.type === "proxy_http_error") {
|
|
21
|
+
if (!pending.res.headersSent) {
|
|
22
|
+
pending.res.writeHead(502, { "Content-Type": "application/json" });
|
|
34
23
|
}
|
|
24
|
+
pending.res.end(JSON.stringify({ error: msg.error }));
|
|
25
|
+
clearTimeout(pending.timeout);
|
|
26
|
+
pendingRequests.delete(msg.requestId);
|
|
35
27
|
}
|
|
36
|
-
return parts.join("\n\n");
|
|
37
28
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (!trimmed) continue;
|
|
48
|
-
try {
|
|
49
|
-
yield JSON.parse(trimmed);
|
|
50
|
-
} catch {
|
|
51
|
-
}
|
|
29
|
+
function startProxyServer(config) {
|
|
30
|
+
const { port, sessionKey, providers, sendToExtension } = config;
|
|
31
|
+
const server = createServer(async (req, res) => {
|
|
32
|
+
const host = req.headers.host || "";
|
|
33
|
+
const hostWithoutPort = host.split(":")[0];
|
|
34
|
+
if (hostWithoutPort !== "127.0.0.1" && hostWithoutPort !== "localhost") {
|
|
35
|
+
res.writeHead(403, { "Content-Type": "application/json" });
|
|
36
|
+
res.end(JSON.stringify({ error: "Forbidden: invalid Host header" }));
|
|
37
|
+
return;
|
|
52
38
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
} catch {
|
|
39
|
+
if (req.method === "OPTIONS") {
|
|
40
|
+
res.writeHead(204);
|
|
41
|
+
res.end();
|
|
42
|
+
return;
|
|
58
43
|
}
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
async function translateRequest(request, setupToken) {
|
|
64
|
-
const messages = request.messages.map((m) => ({
|
|
65
|
-
role: m.role,
|
|
66
|
-
content: typeof m.content === "string" ? m.content : m.content.filter((c) => c.type === "text").map((c) => c.text).join("\n")
|
|
67
|
-
}));
|
|
68
|
-
let system;
|
|
69
|
-
if (typeof request.system === "string") {
|
|
70
|
-
system = request.system;
|
|
71
|
-
} else if (Array.isArray(request.system)) {
|
|
72
|
-
system = request.system.map((s) => s.text).join("\n");
|
|
73
|
-
}
|
|
74
|
-
const claudeRequest = {
|
|
75
|
-
model: request.model,
|
|
76
|
-
messages,
|
|
77
|
-
max_tokens: request.max_tokens,
|
|
78
|
-
system
|
|
79
|
-
};
|
|
80
|
-
const { process: proc, output } = runClaude(claudeRequest, setupToken);
|
|
81
|
-
let fullText = "";
|
|
82
|
-
let inputTokens = 0;
|
|
83
|
-
let outputTokens = 0;
|
|
84
|
-
const streamChunks = [];
|
|
85
|
-
try {
|
|
86
|
-
for await (const event of output) {
|
|
87
|
-
if (event.type === "assistant" && event.content) {
|
|
88
|
-
fullText += event.content;
|
|
89
|
-
if (request.stream) {
|
|
90
|
-
streamChunks.push(
|
|
91
|
-
buildStreamChunk(event.content, request.model)
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
if (event.type === "result") {
|
|
96
|
-
if (event.result && !fullText) {
|
|
97
|
-
fullText = event.result;
|
|
98
|
-
}
|
|
99
|
-
if (event.tokens) {
|
|
100
|
-
inputTokens = event.tokens.input;
|
|
101
|
-
outputTokens = event.tokens.output;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
44
|
+
if (req.url === "/health") {
|
|
45
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
46
|
+
res.end(JSON.stringify({ status: "ok", providers }));
|
|
47
|
+
return;
|
|
104
48
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
49
|
+
const match = req.url?.match(/^\/([^/]+)(\/.*)?$/);
|
|
50
|
+
if (!match) {
|
|
51
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
52
|
+
res.end(JSON.stringify({ error: "Unknown route. Use /<providerId>/..." }));
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const providerId = match[1];
|
|
56
|
+
const path = match[2] || "/";
|
|
57
|
+
if (!providers.includes(providerId)) {
|
|
58
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
59
|
+
res.end(JSON.stringify({ error: `Provider "${providerId}" not available in this session` }));
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const body = await readBody(req);
|
|
63
|
+
const providerUrls = {
|
|
64
|
+
anthropic: "https://api.anthropic.com",
|
|
65
|
+
openai: "https://api.openai.com",
|
|
66
|
+
gemini: "https://generativelanguage.googleapis.com",
|
|
67
|
+
mistral: "https://api.mistral.ai",
|
|
68
|
+
cohere: "https://api.cohere.com",
|
|
69
|
+
xai: "https://api.x.ai",
|
|
70
|
+
deepseek: "https://api.deepseek.com",
|
|
71
|
+
perplexity: "https://api.perplexity.ai",
|
|
72
|
+
groq: "https://api.groq.com",
|
|
73
|
+
together: "https://api.together.xyz",
|
|
74
|
+
fireworks: "https://api.fireworks.ai",
|
|
75
|
+
replicate: "https://api.replicate.com",
|
|
76
|
+
openrouter: "https://openrouter.ai/api",
|
|
77
|
+
huggingface: "https://api-inference.huggingface.co",
|
|
78
|
+
azure_openai: "https://YOUR_RESOURCE.openai.azure.com"
|
|
123
79
|
};
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
80
|
+
const baseUrl = providerUrls[providerId];
|
|
81
|
+
if (!baseUrl) {
|
|
82
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
83
|
+
res.end(JSON.stringify({ error: `Unknown provider base URL for "${providerId}"` }));
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const realUrl = `${baseUrl}${path}`;
|
|
87
|
+
const requestId = `proxy-${crypto.randomUUID()}`;
|
|
88
|
+
const headers = {};
|
|
89
|
+
for (const [key, value] of Object.entries(req.headers)) {
|
|
90
|
+
if (key === "host" || key === "connection") continue;
|
|
91
|
+
if (typeof value === "string") headers[key] = value;
|
|
92
|
+
}
|
|
93
|
+
const proxyMsg = {
|
|
94
|
+
type: "proxy_http",
|
|
95
|
+
requestId,
|
|
96
|
+
sessionKey,
|
|
97
|
+
providerId,
|
|
98
|
+
url: realUrl,
|
|
99
|
+
method: req.method || "GET",
|
|
100
|
+
headers,
|
|
101
|
+
body: body || void 0
|
|
146
102
|
};
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
role: "assistant",
|
|
152
|
-
content: [{ type: "text", text: fullText }],
|
|
153
|
-
model: request.model,
|
|
154
|
-
stop_reason: "end_turn",
|
|
155
|
-
usage: { input_tokens: inputTokens, output_tokens: outputTokens }
|
|
156
|
-
};
|
|
157
|
-
return {
|
|
158
|
-
status: 200,
|
|
159
|
-
headers: { "content-type": "application/json" },
|
|
160
|
-
body: JSON.stringify(response),
|
|
161
|
-
isStream: false
|
|
162
|
-
};
|
|
163
|
-
}
|
|
164
|
-
function buildStreamChunk(text, _model) {
|
|
165
|
-
const data = {
|
|
166
|
-
type: "content_block_delta",
|
|
167
|
-
index: 0,
|
|
168
|
-
delta: { type: "text_delta", text }
|
|
169
|
-
};
|
|
170
|
-
return `event: content_block_delta
|
|
171
|
-
data: ${JSON.stringify(data)}
|
|
172
|
-
|
|
173
|
-
`;
|
|
174
|
-
}
|
|
175
|
-
function buildStreamMessageStart(id, model) {
|
|
176
|
-
const data = {
|
|
177
|
-
type: "message_start",
|
|
178
|
-
message: {
|
|
179
|
-
id,
|
|
180
|
-
type: "message",
|
|
181
|
-
role: "assistant",
|
|
182
|
-
content: [],
|
|
183
|
-
model,
|
|
184
|
-
stop_reason: null,
|
|
185
|
-
usage: { input_tokens: 0, output_tokens: 0 }
|
|
103
|
+
if (pendingRequests.size >= MAX_PENDING_REQUESTS) {
|
|
104
|
+
res.writeHead(503, { "Content-Type": "application/json" });
|
|
105
|
+
res.end(JSON.stringify({ error: "Too many concurrent requests" }));
|
|
106
|
+
return;
|
|
186
107
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
};
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
const stop = {
|
|
205
|
-
type: "content_block_stop",
|
|
206
|
-
index: 0
|
|
207
|
-
};
|
|
208
|
-
return `event: content_block_start
|
|
209
|
-
data: ${JSON.stringify(start)}
|
|
210
|
-
|
|
211
|
-
event: content_block_delta
|
|
212
|
-
data: ${JSON.stringify(delta)}
|
|
213
|
-
|
|
214
|
-
event: content_block_stop
|
|
215
|
-
data: ${JSON.stringify(stop)}
|
|
216
|
-
|
|
217
|
-
`;
|
|
108
|
+
const timeout = setTimeout(() => {
|
|
109
|
+
if (pendingRequests.has(requestId)) {
|
|
110
|
+
pendingRequests.delete(requestId);
|
|
111
|
+
if (!res.headersSent) {
|
|
112
|
+
res.writeHead(504, { "Content-Type": "application/json" });
|
|
113
|
+
}
|
|
114
|
+
res.end(JSON.stringify({ error: "Request timed out" }));
|
|
115
|
+
}
|
|
116
|
+
}, 12e4);
|
|
117
|
+
pendingRequests.set(requestId, { res, timeout });
|
|
118
|
+
sendToExtension(proxyMsg);
|
|
119
|
+
});
|
|
120
|
+
server.listen(port, "127.0.0.1", () => {
|
|
121
|
+
process.stderr.write(`Byoky proxy listening on http://127.0.0.1:${port}
|
|
122
|
+
`);
|
|
123
|
+
});
|
|
124
|
+
return server;
|
|
218
125
|
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
126
|
+
var MAX_BODY_SIZE = 10 * 1024 * 1024;
|
|
127
|
+
function readBody(req) {
|
|
128
|
+
return new Promise((resolve, reject) => {
|
|
129
|
+
let body = "";
|
|
130
|
+
let size = 0;
|
|
131
|
+
req.on("data", (chunk) => {
|
|
132
|
+
size += chunk.length;
|
|
133
|
+
if (size > MAX_BODY_SIZE) {
|
|
134
|
+
req.destroy();
|
|
135
|
+
reject(new Error("Request body too large"));
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
body += chunk.toString();
|
|
139
|
+
});
|
|
140
|
+
req.on("end", () => resolve(body));
|
|
141
|
+
req.on("error", reject);
|
|
142
|
+
});
|
|
229
143
|
}
|
|
230
144
|
|
|
231
145
|
// src/host.ts
|
|
@@ -250,9 +164,13 @@ function readMessage() {
|
|
|
250
164
|
resolve(null);
|
|
251
165
|
return;
|
|
252
166
|
}
|
|
253
|
-
|
|
167
|
+
if (msgLength > 1048576) {
|
|
168
|
+
reject(new Error(`Message too large: ${msgLength} bytes (max 1MB)`));
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
readBody2(msgLength);
|
|
254
172
|
}
|
|
255
|
-
function
|
|
173
|
+
function readBody2(length) {
|
|
256
174
|
let body = Buffer.alloc(0);
|
|
257
175
|
function readChunk() {
|
|
258
176
|
const chunk = process.stdin.read(length - body.length);
|
|
@@ -289,30 +207,80 @@ async function handleMessage(msg) {
|
|
|
289
207
|
if (!msg || typeof msg !== "object") return;
|
|
290
208
|
const message = msg;
|
|
291
209
|
if (message.type === "ping") {
|
|
292
|
-
writeMessage({ type: "pong", version: "0.
|
|
210
|
+
writeMessage({ type: "pong", version: "0.3.0" });
|
|
293
211
|
return;
|
|
294
212
|
}
|
|
295
213
|
if (message.type === "proxy") {
|
|
296
214
|
const req = msg;
|
|
297
|
-
await
|
|
215
|
+
await handleStreamingFetch(req.requestId, req.url, req.method, req.headers, req.body, "bridge");
|
|
298
216
|
return;
|
|
299
217
|
}
|
|
218
|
+
if (message.type === "proxy_direct_fetch") {
|
|
219
|
+
const req = msg;
|
|
220
|
+
await handleStreamingFetch(req.requestId, req.url, req.method, req.headers, req.body, "proxy_http");
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
if (message.type === "start-proxy") {
|
|
224
|
+
const req = msg;
|
|
225
|
+
handleStartProxy(req);
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
if (msg && typeof msg === "object" && "type" in msg && (msg.type === "proxy_http_response_meta" || msg.type === "proxy_http_response_chunk" || msg.type === "proxy_http_response_done" || msg.type === "proxy_http_error")) {
|
|
229
|
+
handleProxyResponse(msg);
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
async function handleStreamingFetch(requestId, url, method, headers, body, mode) {
|
|
234
|
+
const send = (msg) => {
|
|
235
|
+
if (mode === "bridge") {
|
|
236
|
+
writeMessage(msg);
|
|
237
|
+
} else {
|
|
238
|
+
handleProxyResponse(msg);
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
const prefix = mode === "bridge" ? "proxy_response" : "proxy_http_response";
|
|
242
|
+
const errorType = mode === "bridge" ? "proxy_error" : "proxy_http_error";
|
|
243
|
+
try {
|
|
244
|
+
const res = await fetch(url, {
|
|
245
|
+
method,
|
|
246
|
+
headers,
|
|
247
|
+
body: body || void 0
|
|
248
|
+
});
|
|
249
|
+
const resHeaders = {};
|
|
250
|
+
res.headers.forEach((v, k) => {
|
|
251
|
+
resHeaders[k] = v;
|
|
252
|
+
});
|
|
253
|
+
send({ type: `${prefix}_meta`, requestId, status: res.status, headers: resHeaders });
|
|
254
|
+
if (res.body) {
|
|
255
|
+
const reader = res.body.getReader();
|
|
256
|
+
const decoder = new TextDecoder();
|
|
257
|
+
for (; ; ) {
|
|
258
|
+
const { done, value } = await reader.read();
|
|
259
|
+
if (done) break;
|
|
260
|
+
send({ type: `${prefix}_chunk`, requestId, chunk: decoder.decode(value, { stream: true }) });
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
send({ type: `${prefix}_done`, requestId });
|
|
264
|
+
} catch (e) {
|
|
265
|
+
send({ type: errorType, requestId, error: e.message });
|
|
266
|
+
}
|
|
300
267
|
}
|
|
301
|
-
|
|
268
|
+
function handleStartProxy(req) {
|
|
302
269
|
try {
|
|
303
|
-
|
|
304
|
-
|
|
270
|
+
startProxyServer({
|
|
271
|
+
port: req.port,
|
|
272
|
+
sessionKey: req.sessionKey,
|
|
273
|
+
providers: req.providers,
|
|
274
|
+
sendToExtension: (msg) => writeMessage(msg)
|
|
275
|
+
});
|
|
305
276
|
writeMessage({
|
|
306
|
-
type: "
|
|
307
|
-
|
|
308
|
-
status: result.status,
|
|
309
|
-
headers: result.headers,
|
|
310
|
-
body: result.body
|
|
277
|
+
type: "proxy-started",
|
|
278
|
+
port: req.port
|
|
311
279
|
});
|
|
312
280
|
} catch (e) {
|
|
313
281
|
writeMessage({
|
|
314
282
|
type: "proxy_error",
|
|
315
|
-
requestId:
|
|
283
|
+
requestId: "start-proxy",
|
|
316
284
|
error: e.message
|
|
317
285
|
});
|
|
318
286
|
}
|
package/dist/installer.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* byoky-bridge uninstall — remove registrations
|
|
7
7
|
* byoky-bridge status — check if registered
|
|
8
8
|
*/
|
|
9
|
-
declare function install(): void;
|
|
9
|
+
declare function install(extensionId?: string): void;
|
|
10
10
|
declare function uninstall(): void;
|
|
11
11
|
declare function status(): void;
|
|
12
12
|
|
package/dist/installer.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/installer.ts
|
|
2
|
-
import { writeFileSync, mkdirSync, unlinkSync, existsSync } from "fs";
|
|
2
|
+
import { writeFileSync, mkdirSync, unlinkSync, existsSync, chmodSync } from "fs";
|
|
3
3
|
import { dirname, resolve } from "path";
|
|
4
4
|
import { homedir, platform } from "os";
|
|
5
5
|
import { execSync } from "child_process";
|
|
@@ -11,7 +11,20 @@ function getHostPath() {
|
|
|
11
11
|
return resolve(dirname(new URL(import.meta.url).pathname), "../bin/byoky-bridge.js");
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
|
-
function
|
|
14
|
+
function createNativeWrapper(hostPath, manifestDir) {
|
|
15
|
+
const nodePath = process.execPath;
|
|
16
|
+
const wrapperPath = resolve(manifestDir, "byoky-bridge-host");
|
|
17
|
+
const userPath = process.env.PATH || "";
|
|
18
|
+
const script = `#!/bin/bash
|
|
19
|
+
export PATH="${userPath}"
|
|
20
|
+
exec "${nodePath}" "${hostPath}" host "$@"
|
|
21
|
+
`;
|
|
22
|
+
writeFileSync(wrapperPath, script);
|
|
23
|
+
chmodSync(wrapperPath, 493);
|
|
24
|
+
return wrapperPath;
|
|
25
|
+
}
|
|
26
|
+
var DEFAULT_EXTENSION_ID = "ahhecmfcclkjdgjnmackoacldnmgmipl";
|
|
27
|
+
function buildManifest(hostPath, browserType, extensionId) {
|
|
15
28
|
const base = {
|
|
16
29
|
name: HOST_NAME,
|
|
17
30
|
description: "Byoky Bridge \u2014 routes setup token requests through Claude Code CLI",
|
|
@@ -19,11 +32,11 @@ function buildManifest(hostPath, browserType) {
|
|
|
19
32
|
type: "stdio"
|
|
20
33
|
};
|
|
21
34
|
if (browserType === "chrome") {
|
|
35
|
+
const id = extensionId || DEFAULT_EXTENSION_ID;
|
|
22
36
|
return {
|
|
23
37
|
...base,
|
|
24
38
|
allowed_origins: [
|
|
25
|
-
|
|
26
|
-
"chrome-extension://*/"
|
|
39
|
+
`chrome-extension://${id}/`
|
|
27
40
|
]
|
|
28
41
|
};
|
|
29
42
|
}
|
|
@@ -95,7 +108,7 @@ function getManifestLocations() {
|
|
|
95
108
|
}
|
|
96
109
|
return [];
|
|
97
110
|
}
|
|
98
|
-
function install() {
|
|
111
|
+
function install(extensionId) {
|
|
99
112
|
const hostPath = getHostPath();
|
|
100
113
|
const locations = getManifestLocations();
|
|
101
114
|
if (locations.length === 0) {
|
|
@@ -105,8 +118,10 @@ function install() {
|
|
|
105
118
|
let installed = 0;
|
|
106
119
|
for (const loc of locations) {
|
|
107
120
|
try {
|
|
108
|
-
const
|
|
109
|
-
mkdirSync(
|
|
121
|
+
const manifestDir = dirname(loc.path);
|
|
122
|
+
mkdirSync(manifestDir, { recursive: true });
|
|
123
|
+
const wrapperPath = createNativeWrapper(hostPath, manifestDir);
|
|
124
|
+
const manifest = buildManifest(wrapperPath, loc.type, extensionId);
|
|
110
125
|
writeFileSync(loc.path, JSON.stringify(manifest, null, 2));
|
|
111
126
|
console.log(` Registered with ${loc.browser}`);
|
|
112
127
|
installed++;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@byoky/bridge",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Native messaging bridge for Byoky — routes setup token requests through Claude Code CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/host.js",
|
|
@@ -11,10 +11,6 @@
|
|
|
11
11
|
"dist",
|
|
12
12
|
"bin"
|
|
13
13
|
],
|
|
14
|
-
"scripts": {
|
|
15
|
-
"build": "tsup",
|
|
16
|
-
"dev": "tsup --watch"
|
|
17
|
-
},
|
|
18
14
|
"keywords": [
|
|
19
15
|
"byoky",
|
|
20
16
|
"claude",
|
|
@@ -31,5 +27,9 @@
|
|
|
31
27
|
"@types/node": "25.5.0",
|
|
32
28
|
"tsup": "^8.0.0",
|
|
33
29
|
"typescript": "^5.5.0"
|
|
30
|
+
},
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "tsup",
|
|
33
|
+
"dev": "tsup --watch"
|
|
34
34
|
}
|
|
35
|
-
}
|
|
35
|
+
}
|