@byoky/bridge 0.2.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/bin/byoky-bridge.js +26 -0
- package/dist/host.d.ts +2 -0
- package/dist/host.js +333 -0
- package/dist/installer.d.ts +13 -0
- package/dist/installer.js +149 -0
- package/package.json +35 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const command = process.argv[2];
|
|
4
|
+
|
|
5
|
+
if (command === 'install' || command === 'uninstall' || command === 'status') {
|
|
6
|
+
// CLI mode: manage native messaging registration
|
|
7
|
+
const { install, uninstall, status } = await import('../dist/installer.js');
|
|
8
|
+
|
|
9
|
+
console.log('Byoky Bridge\n');
|
|
10
|
+
|
|
11
|
+
if (command === 'install') install();
|
|
12
|
+
else if (command === 'uninstall') uninstall();
|
|
13
|
+
else if (command === 'status') status();
|
|
14
|
+
} else if (!command || command === 'host') {
|
|
15
|
+
// Native messaging host mode (called by browser)
|
|
16
|
+
await import('../dist/host.js');
|
|
17
|
+
} else {
|
|
18
|
+
console.log(`Usage: byoky-bridge <command>
|
|
19
|
+
|
|
20
|
+
Commands:
|
|
21
|
+
install Register native messaging host with browsers
|
|
22
|
+
uninstall Remove native messaging registration
|
|
23
|
+
status Check registration status
|
|
24
|
+
|
|
25
|
+
The bridge runs automatically when called by the Byoky extension.`);
|
|
26
|
+
}
|
package/dist/host.d.ts
ADDED
package/dist/host.js
ADDED
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
// src/claude-runner.ts
|
|
2
|
+
import { spawn } from "child_process";
|
|
3
|
+
function runClaude(request, setupToken) {
|
|
4
|
+
const prompt = formatMessagesAsPrompt(request);
|
|
5
|
+
const args = ["-p", "--output-format", "stream-json"];
|
|
6
|
+
if (request.max_tokens) {
|
|
7
|
+
args.push("--max-tokens", String(request.max_tokens));
|
|
8
|
+
}
|
|
9
|
+
if (request.model) {
|
|
10
|
+
args.push("--model", request.model);
|
|
11
|
+
}
|
|
12
|
+
const proc = spawn("claude", args, {
|
|
13
|
+
env: {
|
|
14
|
+
...process.env,
|
|
15
|
+
ANTHROPIC_AUTH_TOKEN: setupToken
|
|
16
|
+
},
|
|
17
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
18
|
+
});
|
|
19
|
+
proc.stdin.write(prompt);
|
|
20
|
+
proc.stdin.end();
|
|
21
|
+
const output = streamEvents(proc);
|
|
22
|
+
return { process: proc, output };
|
|
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}]`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return parts.join("\n\n");
|
|
37
|
+
}
|
|
38
|
+
async function* streamEvents(proc) {
|
|
39
|
+
if (!proc.stdout) return;
|
|
40
|
+
let buffer = "";
|
|
41
|
+
for await (const chunk of proc.stdout) {
|
|
42
|
+
buffer += chunk.toString();
|
|
43
|
+
const lines = buffer.split("\n");
|
|
44
|
+
buffer = lines.pop() ?? "";
|
|
45
|
+
for (const line of lines) {
|
|
46
|
+
const trimmed = line.trim();
|
|
47
|
+
if (!trimmed) continue;
|
|
48
|
+
try {
|
|
49
|
+
yield JSON.parse(trimmed);
|
|
50
|
+
} catch {
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (buffer.trim()) {
|
|
55
|
+
try {
|
|
56
|
+
yield JSON.parse(buffer.trim());
|
|
57
|
+
} catch {
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// src/translator.ts
|
|
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
|
+
}
|
|
104
|
+
}
|
|
105
|
+
} catch {
|
|
106
|
+
}
|
|
107
|
+
await new Promise((resolve) => {
|
|
108
|
+
proc.on("close", () => resolve());
|
|
109
|
+
if (proc.exitCode !== null) resolve();
|
|
110
|
+
});
|
|
111
|
+
if (!fullText && proc.exitCode !== 0) {
|
|
112
|
+
return {
|
|
113
|
+
status: 500,
|
|
114
|
+
headers: { "content-type": "application/json" },
|
|
115
|
+
body: JSON.stringify({
|
|
116
|
+
type: "error",
|
|
117
|
+
error: {
|
|
118
|
+
type: "api_error",
|
|
119
|
+
message: "Claude Code process failed. Is Claude Code installed and is the setup token valid?"
|
|
120
|
+
}
|
|
121
|
+
}),
|
|
122
|
+
isStream: false
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
const responseId = `msg_byoky_${Date.now().toString(36)}`;
|
|
126
|
+
if (request.stream) {
|
|
127
|
+
streamChunks.push(
|
|
128
|
+
buildStreamMessageStart(responseId, request.model)
|
|
129
|
+
);
|
|
130
|
+
streamChunks.push(
|
|
131
|
+
buildStreamContentDelta(fullText)
|
|
132
|
+
);
|
|
133
|
+
streamChunks.push(
|
|
134
|
+
buildStreamMessageDelta(inputTokens, outputTokens)
|
|
135
|
+
);
|
|
136
|
+
streamChunks.push('event: message_stop\ndata: {"type":"message_stop"}\n\n');
|
|
137
|
+
return {
|
|
138
|
+
status: 200,
|
|
139
|
+
headers: {
|
|
140
|
+
"content-type": "text/event-stream",
|
|
141
|
+
"cache-control": "no-cache"
|
|
142
|
+
},
|
|
143
|
+
body: streamChunks.join(""),
|
|
144
|
+
isStream: true,
|
|
145
|
+
streamChunks
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
const response = {
|
|
149
|
+
id: responseId,
|
|
150
|
+
type: "message",
|
|
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 }
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
return `event: message_start
|
|
189
|
+
data: ${JSON.stringify(data)}
|
|
190
|
+
|
|
191
|
+
`;
|
|
192
|
+
}
|
|
193
|
+
function buildStreamContentDelta(text) {
|
|
194
|
+
const start = {
|
|
195
|
+
type: "content_block_start",
|
|
196
|
+
index: 0,
|
|
197
|
+
content_block: { type: "text", text: "" }
|
|
198
|
+
};
|
|
199
|
+
const delta = {
|
|
200
|
+
type: "content_block_delta",
|
|
201
|
+
index: 0,
|
|
202
|
+
delta: { type: "text_delta", text }
|
|
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
|
+
`;
|
|
218
|
+
}
|
|
219
|
+
function buildStreamMessageDelta(inputTokens, outputTokens) {
|
|
220
|
+
const data = {
|
|
221
|
+
type: "message_delta",
|
|
222
|
+
delta: { stop_reason: "end_turn" },
|
|
223
|
+
usage: { input_tokens: inputTokens, output_tokens: outputTokens }
|
|
224
|
+
};
|
|
225
|
+
return `event: message_delta
|
|
226
|
+
data: ${JSON.stringify(data)}
|
|
227
|
+
|
|
228
|
+
`;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// src/host.ts
|
|
232
|
+
function readMessage() {
|
|
233
|
+
return new Promise((resolve, reject) => {
|
|
234
|
+
const lengthBuf = Buffer.alloc(4);
|
|
235
|
+
let bytesRead = 0;
|
|
236
|
+
function readLength() {
|
|
237
|
+
const chunk = process.stdin.read(4 - bytesRead);
|
|
238
|
+
if (!chunk) {
|
|
239
|
+
process.stdin.once("readable", readLength);
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
chunk.copy(lengthBuf, bytesRead);
|
|
243
|
+
bytesRead += chunk.length;
|
|
244
|
+
if (bytesRead < 4) {
|
|
245
|
+
process.stdin.once("readable", readLength);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
const msgLength = lengthBuf.readUInt32LE(0);
|
|
249
|
+
if (msgLength === 0) {
|
|
250
|
+
resolve(null);
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
readBody(msgLength);
|
|
254
|
+
}
|
|
255
|
+
function readBody(length) {
|
|
256
|
+
let body = Buffer.alloc(0);
|
|
257
|
+
function readChunk() {
|
|
258
|
+
const chunk = process.stdin.read(length - body.length);
|
|
259
|
+
if (!chunk) {
|
|
260
|
+
process.stdin.once("readable", readChunk);
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
body = Buffer.concat([body, chunk]);
|
|
264
|
+
if (body.length < length) {
|
|
265
|
+
process.stdin.once("readable", readChunk);
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
try {
|
|
269
|
+
resolve(JSON.parse(body.toString("utf-8")));
|
|
270
|
+
} catch (e) {
|
|
271
|
+
reject(new Error(`Invalid JSON: ${e.message}`));
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
readChunk();
|
|
275
|
+
}
|
|
276
|
+
process.stdin.once("readable", readLength);
|
|
277
|
+
process.stdin.on("end", () => resolve(null));
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
function writeMessage(msg) {
|
|
281
|
+
const json = JSON.stringify(msg);
|
|
282
|
+
const buf = Buffer.from(json, "utf-8");
|
|
283
|
+
const lengthBuf = Buffer.alloc(4);
|
|
284
|
+
lengthBuf.writeUInt32LE(buf.length, 0);
|
|
285
|
+
process.stdout.write(lengthBuf);
|
|
286
|
+
process.stdout.write(buf);
|
|
287
|
+
}
|
|
288
|
+
async function handleMessage(msg) {
|
|
289
|
+
if (!msg || typeof msg !== "object") return;
|
|
290
|
+
const message = msg;
|
|
291
|
+
if (message.type === "ping") {
|
|
292
|
+
writeMessage({ type: "pong", version: "0.2.0" });
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
if (message.type === "proxy") {
|
|
296
|
+
const req = msg;
|
|
297
|
+
await handleProxy(req);
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
async function handleProxy(req) {
|
|
302
|
+
try {
|
|
303
|
+
const apiRequest = JSON.parse(req.body);
|
|
304
|
+
const result = await translateRequest(apiRequest, req.setupToken);
|
|
305
|
+
writeMessage({
|
|
306
|
+
type: "proxy_response",
|
|
307
|
+
requestId: req.requestId,
|
|
308
|
+
status: result.status,
|
|
309
|
+
headers: result.headers,
|
|
310
|
+
body: result.body
|
|
311
|
+
});
|
|
312
|
+
} catch (e) {
|
|
313
|
+
writeMessage({
|
|
314
|
+
type: "proxy_error",
|
|
315
|
+
requestId: req.requestId,
|
|
316
|
+
error: e.message
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
async function main() {
|
|
321
|
+
process.stdin.resume();
|
|
322
|
+
while (true) {
|
|
323
|
+
const msg = await readMessage();
|
|
324
|
+
if (msg === null) break;
|
|
325
|
+
await handleMessage(msg);
|
|
326
|
+
}
|
|
327
|
+
process.exit(0);
|
|
328
|
+
}
|
|
329
|
+
main().catch((e) => {
|
|
330
|
+
process.stderr.write(`byoky-bridge fatal: ${e.message}
|
|
331
|
+
`);
|
|
332
|
+
process.exit(1);
|
|
333
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Installs/uninstalls the native messaging host manifest for each browser.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* byoky-bridge install — register with Chrome, Firefox, and Safari
|
|
6
|
+
* byoky-bridge uninstall — remove registrations
|
|
7
|
+
* byoky-bridge status — check if registered
|
|
8
|
+
*/
|
|
9
|
+
declare function install(): void;
|
|
10
|
+
declare function uninstall(): void;
|
|
11
|
+
declare function status(): void;
|
|
12
|
+
|
|
13
|
+
export { install, status, uninstall };
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
// src/installer.ts
|
|
2
|
+
import { writeFileSync, mkdirSync, unlinkSync, existsSync } from "fs";
|
|
3
|
+
import { dirname, resolve } from "path";
|
|
4
|
+
import { homedir, platform } from "os";
|
|
5
|
+
import { execSync } from "child_process";
|
|
6
|
+
var HOST_NAME = "com.byoky.bridge";
|
|
7
|
+
function getHostPath() {
|
|
8
|
+
try {
|
|
9
|
+
return execSync("which byoky-bridge", { encoding: "utf-8" }).trim();
|
|
10
|
+
} catch {
|
|
11
|
+
return resolve(dirname(new URL(import.meta.url).pathname), "../bin/byoky-bridge.js");
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function buildManifest(hostPath, browserType) {
|
|
15
|
+
const base = {
|
|
16
|
+
name: HOST_NAME,
|
|
17
|
+
description: "Byoky Bridge \u2014 routes setup token requests through Claude Code CLI",
|
|
18
|
+
path: hostPath,
|
|
19
|
+
type: "stdio"
|
|
20
|
+
};
|
|
21
|
+
if (browserType === "chrome") {
|
|
22
|
+
return {
|
|
23
|
+
...base,
|
|
24
|
+
allowed_origins: [
|
|
25
|
+
// Chrome uses extension IDs — we allow all since the ID varies per install
|
|
26
|
+
"chrome-extension://*/"
|
|
27
|
+
]
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
...base,
|
|
32
|
+
allowed_extensions: ["byoky@byoky.com"]
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function getManifestLocations() {
|
|
36
|
+
const home = homedir();
|
|
37
|
+
const os = platform();
|
|
38
|
+
if (os === "darwin") {
|
|
39
|
+
return [
|
|
40
|
+
{
|
|
41
|
+
browser: "Chrome",
|
|
42
|
+
path: `${home}/Library/Application Support/Google/Chrome/NativeMessagingHosts/${HOST_NAME}.json`,
|
|
43
|
+
type: "chrome"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
browser: "Chromium",
|
|
47
|
+
path: `${home}/Library/Application Support/Chromium/NativeMessagingHosts/${HOST_NAME}.json`,
|
|
48
|
+
type: "chrome"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
browser: "Brave",
|
|
52
|
+
path: `${home}/Library/Application Support/BraveSoftware/Brave-Browser/NativeMessagingHosts/${HOST_NAME}.json`,
|
|
53
|
+
type: "chrome"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
browser: "Firefox",
|
|
57
|
+
path: `${home}/Library/Application Support/Mozilla/NativeMessagingHosts/${HOST_NAME}.json`,
|
|
58
|
+
type: "firefox"
|
|
59
|
+
}
|
|
60
|
+
];
|
|
61
|
+
}
|
|
62
|
+
if (os === "linux") {
|
|
63
|
+
return [
|
|
64
|
+
{
|
|
65
|
+
browser: "Chrome",
|
|
66
|
+
path: `${home}/.config/google-chrome/NativeMessagingHosts/${HOST_NAME}.json`,
|
|
67
|
+
type: "chrome"
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
browser: "Chromium",
|
|
71
|
+
path: `${home}/.config/chromium/NativeMessagingHosts/${HOST_NAME}.json`,
|
|
72
|
+
type: "chrome"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
browser: "Firefox",
|
|
76
|
+
path: `${home}/.mozilla/native-messaging-hosts/${HOST_NAME}.json`,
|
|
77
|
+
type: "firefox"
|
|
78
|
+
}
|
|
79
|
+
];
|
|
80
|
+
}
|
|
81
|
+
if (os === "win32") {
|
|
82
|
+
const appData = process.env.LOCALAPPDATA || `${home}/AppData/Local`;
|
|
83
|
+
return [
|
|
84
|
+
{
|
|
85
|
+
browser: "Chrome",
|
|
86
|
+
path: `${appData}/Google/Chrome/User Data/NativeMessagingHosts/${HOST_NAME}.json`,
|
|
87
|
+
type: "chrome"
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
browser: "Firefox",
|
|
91
|
+
path: `${appData}/Mozilla/NativeMessagingHosts/${HOST_NAME}.json`,
|
|
92
|
+
type: "firefox"
|
|
93
|
+
}
|
|
94
|
+
];
|
|
95
|
+
}
|
|
96
|
+
return [];
|
|
97
|
+
}
|
|
98
|
+
function install() {
|
|
99
|
+
const hostPath = getHostPath();
|
|
100
|
+
const locations = getManifestLocations();
|
|
101
|
+
if (locations.length === 0) {
|
|
102
|
+
console.error("Unsupported platform");
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
let installed = 0;
|
|
106
|
+
for (const loc of locations) {
|
|
107
|
+
try {
|
|
108
|
+
const manifest = buildManifest(hostPath, loc.type);
|
|
109
|
+
mkdirSync(dirname(loc.path), { recursive: true });
|
|
110
|
+
writeFileSync(loc.path, JSON.stringify(manifest, null, 2));
|
|
111
|
+
console.log(` Registered with ${loc.browser}`);
|
|
112
|
+
installed++;
|
|
113
|
+
} catch {
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (installed > 0) {
|
|
117
|
+
console.log(`
|
|
118
|
+
Byoky Bridge installed for ${installed} browser(s).`);
|
|
119
|
+
console.log("Restart your browser for changes to take effect.");
|
|
120
|
+
} else {
|
|
121
|
+
console.error("No supported browsers found.");
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
function uninstall() {
|
|
125
|
+
const locations = getManifestLocations();
|
|
126
|
+
for (const loc of locations) {
|
|
127
|
+
try {
|
|
128
|
+
if (existsSync(loc.path)) {
|
|
129
|
+
unlinkSync(loc.path);
|
|
130
|
+
console.log(` Removed from ${loc.browser}`);
|
|
131
|
+
}
|
|
132
|
+
} catch {
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
console.log("\nByoky Bridge uninstalled.");
|
|
136
|
+
}
|
|
137
|
+
function status() {
|
|
138
|
+
const locations = getManifestLocations();
|
|
139
|
+
for (const loc of locations) {
|
|
140
|
+
const exists = existsSync(loc.path);
|
|
141
|
+
const icon = exists ? "\u2713" : "\u2717";
|
|
142
|
+
console.log(` ${icon} ${loc.browser}: ${exists ? "registered" : "not registered"}`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
export {
|
|
146
|
+
install,
|
|
147
|
+
status,
|
|
148
|
+
uninstall
|
|
149
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@byoky/bridge",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Native messaging bridge for Byoky — routes setup token requests through Claude Code CLI",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/host.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"byoky-bridge": "./bin/byoky-bridge.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"bin"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsup",
|
|
16
|
+
"dev": "tsup --watch"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"byoky",
|
|
20
|
+
"claude",
|
|
21
|
+
"native-messaging",
|
|
22
|
+
"bridge"
|
|
23
|
+
],
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "https://github.com/MichaelLod/byoky.git",
|
|
28
|
+
"directory": "packages/bridge"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/node": "25.5.0",
|
|
32
|
+
"tsup": "^8.0.0",
|
|
33
|
+
"typescript": "^5.5.0"
|
|
34
|
+
}
|
|
35
|
+
}
|