@byoky/bridge 0.2.0 → 0.3.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 +6 -1
- package/dist/host.js +181 -227
- package/dist/installer.d.ts +1 -1
- package/dist/installer.js +22 -7
- package/package.json +1 -1
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,144 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
if (
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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}]`);
|
|
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
|
+
if (msg.type === "proxy_http_response") {
|
|
7
|
+
const pending = pendingRequests.get(msg.requestId);
|
|
8
|
+
if (pending) {
|
|
9
|
+
pending.resolve(msg);
|
|
10
|
+
pendingRequests.delete(msg.requestId);
|
|
11
|
+
}
|
|
12
|
+
} else if (msg.type === "proxy_http_error") {
|
|
13
|
+
const pending = pendingRequests.get(msg.requestId);
|
|
14
|
+
if (pending) {
|
|
15
|
+
pending.reject(new Error(msg.error));
|
|
16
|
+
pendingRequests.delete(msg.requestId);
|
|
34
17
|
}
|
|
35
18
|
}
|
|
36
|
-
return parts.join("\n\n");
|
|
37
19
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (!trimmed) continue;
|
|
48
|
-
try {
|
|
49
|
-
yield JSON.parse(trimmed);
|
|
50
|
-
} catch {
|
|
51
|
-
}
|
|
20
|
+
function startProxyServer(config) {
|
|
21
|
+
const { port, sessionKey, providers, sendToExtension } = config;
|
|
22
|
+
const server = createServer(async (req, res) => {
|
|
23
|
+
const host = req.headers.host || "";
|
|
24
|
+
const hostWithoutPort = host.split(":")[0];
|
|
25
|
+
if (hostWithoutPort !== "127.0.0.1" && hostWithoutPort !== "localhost") {
|
|
26
|
+
res.writeHead(403, { "Content-Type": "application/json" });
|
|
27
|
+
res.end(JSON.stringify({ error: "Forbidden: invalid Host header" }));
|
|
28
|
+
return;
|
|
52
29
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
} catch {
|
|
30
|
+
if (req.method === "OPTIONS") {
|
|
31
|
+
res.writeHead(204);
|
|
32
|
+
res.end();
|
|
33
|
+
return;
|
|
58
34
|
}
|
|
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
|
-
}
|
|
35
|
+
if (req.url === "/health") {
|
|
36
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
37
|
+
res.end(JSON.stringify({ status: "ok", providers }));
|
|
38
|
+
return;
|
|
104
39
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
40
|
+
const match = req.url?.match(/^\/([^/]+)(\/.*)?$/);
|
|
41
|
+
if (!match) {
|
|
42
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
43
|
+
res.end(JSON.stringify({ error: "Unknown route. Use /<providerId>/..." }));
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const providerId = match[1];
|
|
47
|
+
const path = match[2] || "/";
|
|
48
|
+
if (!providers.includes(providerId)) {
|
|
49
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
50
|
+
res.end(JSON.stringify({ error: `Provider "${providerId}" not available in this session` }));
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const body = await readBody(req);
|
|
54
|
+
const providerUrls = {
|
|
55
|
+
anthropic: "https://api.anthropic.com",
|
|
56
|
+
openai: "https://api.openai.com",
|
|
57
|
+
gemini: "https://generativelanguage.googleapis.com",
|
|
58
|
+
mistral: "https://api.mistral.ai",
|
|
59
|
+
cohere: "https://api.cohere.com",
|
|
60
|
+
xai: "https://api.x.ai",
|
|
61
|
+
deepseek: "https://api.deepseek.com",
|
|
62
|
+
perplexity: "https://api.perplexity.ai",
|
|
63
|
+
groq: "https://api.groq.com",
|
|
64
|
+
together: "https://api.together.xyz",
|
|
65
|
+
fireworks: "https://api.fireworks.ai",
|
|
66
|
+
replicate: "https://api.replicate.com",
|
|
67
|
+
openrouter: "https://openrouter.ai/api",
|
|
68
|
+
huggingface: "https://api-inference.huggingface.co",
|
|
69
|
+
azure_openai: "https://YOUR_RESOURCE.openai.azure.com"
|
|
123
70
|
};
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
71
|
+
const baseUrl = providerUrls[providerId];
|
|
72
|
+
if (!baseUrl) {
|
|
73
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
74
|
+
res.end(JSON.stringify({ error: `Unknown provider base URL for "${providerId}"` }));
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const realUrl = `${baseUrl}${path}`;
|
|
78
|
+
const requestId = `proxy-${crypto.randomUUID()}`;
|
|
79
|
+
const headers = {};
|
|
80
|
+
for (const [key, value] of Object.entries(req.headers)) {
|
|
81
|
+
if (key === "host" || key === "connection") continue;
|
|
82
|
+
if (typeof value === "string") headers[key] = value;
|
|
83
|
+
}
|
|
84
|
+
const proxyMsg = {
|
|
85
|
+
type: "proxy_http",
|
|
86
|
+
requestId,
|
|
87
|
+
sessionKey,
|
|
88
|
+
providerId,
|
|
89
|
+
url: realUrl,
|
|
90
|
+
method: req.method || "GET",
|
|
91
|
+
headers,
|
|
92
|
+
body: body || void 0
|
|
146
93
|
};
|
|
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 }
|
|
94
|
+
if (pendingRequests.size >= MAX_PENDING_REQUESTS) {
|
|
95
|
+
res.writeHead(503, { "Content-Type": "application/json" });
|
|
96
|
+
res.end(JSON.stringify({ error: "Too many concurrent requests" }));
|
|
97
|
+
return;
|
|
186
98
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
data: ${JSON.stringify(delta)}
|
|
213
|
-
|
|
214
|
-
event: content_block_stop
|
|
215
|
-
data: ${JSON.stringify(stop)}
|
|
216
|
-
|
|
217
|
-
`;
|
|
99
|
+
try {
|
|
100
|
+
const response = await new Promise((resolve, reject) => {
|
|
101
|
+
pendingRequests.set(requestId, { resolve, reject });
|
|
102
|
+
setTimeout(() => {
|
|
103
|
+
if (pendingRequests.has(requestId)) {
|
|
104
|
+
pendingRequests.delete(requestId);
|
|
105
|
+
reject(new Error("Request timed out"));
|
|
106
|
+
}
|
|
107
|
+
}, 12e4);
|
|
108
|
+
sendToExtension(proxyMsg);
|
|
109
|
+
});
|
|
110
|
+
const responseHeaders = { ...response.headers };
|
|
111
|
+
delete responseHeaders["transfer-encoding"];
|
|
112
|
+
res.writeHead(response.status, responseHeaders);
|
|
113
|
+
res.end(response.body);
|
|
114
|
+
} catch (err) {
|
|
115
|
+
res.writeHead(502, { "Content-Type": "application/json" });
|
|
116
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
server.listen(port, "127.0.0.1", () => {
|
|
120
|
+
process.stderr.write(`Byoky proxy listening on http://127.0.0.1:${port}
|
|
121
|
+
`);
|
|
122
|
+
});
|
|
123
|
+
return server;
|
|
218
124
|
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
125
|
+
var MAX_BODY_SIZE = 10 * 1024 * 1024;
|
|
126
|
+
function readBody(req) {
|
|
127
|
+
return new Promise((resolve, reject) => {
|
|
128
|
+
let body = "";
|
|
129
|
+
let size = 0;
|
|
130
|
+
req.on("data", (chunk) => {
|
|
131
|
+
size += chunk.length;
|
|
132
|
+
if (size > MAX_BODY_SIZE) {
|
|
133
|
+
req.destroy();
|
|
134
|
+
reject(new Error("Request body too large"));
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
body += chunk.toString();
|
|
138
|
+
});
|
|
139
|
+
req.on("end", () => resolve(body));
|
|
140
|
+
req.on("error", reject);
|
|
141
|
+
});
|
|
229
142
|
}
|
|
230
143
|
|
|
231
144
|
// src/host.ts
|
|
@@ -250,9 +163,13 @@ function readMessage() {
|
|
|
250
163
|
resolve(null);
|
|
251
164
|
return;
|
|
252
165
|
}
|
|
253
|
-
|
|
166
|
+
if (msgLength > 1048576) {
|
|
167
|
+
reject(new Error(`Message too large: ${msgLength} bytes (max 1MB)`));
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
readBody2(msgLength);
|
|
254
171
|
}
|
|
255
|
-
function
|
|
172
|
+
function readBody2(length) {
|
|
256
173
|
let body = Buffer.alloc(0);
|
|
257
174
|
function readChunk() {
|
|
258
175
|
const chunk = process.stdin.read(length - body.length);
|
|
@@ -294,20 +211,37 @@ async function handleMessage(msg) {
|
|
|
294
211
|
}
|
|
295
212
|
if (message.type === "proxy") {
|
|
296
213
|
const req = msg;
|
|
297
|
-
await
|
|
214
|
+
await handleSetupTokenProxy(req);
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
if (message.type === "start-proxy") {
|
|
218
|
+
const req = msg;
|
|
219
|
+
handleStartProxy(req);
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
if (message.type === "proxy_http_response" || message.type === "proxy_http_error") {
|
|
223
|
+
handleProxyResponse(msg);
|
|
298
224
|
return;
|
|
299
225
|
}
|
|
300
226
|
}
|
|
301
|
-
async function
|
|
227
|
+
async function handleSetupTokenProxy(req) {
|
|
302
228
|
try {
|
|
303
|
-
const
|
|
304
|
-
|
|
229
|
+
const res = await fetch(req.url, {
|
|
230
|
+
method: req.method,
|
|
231
|
+
headers: req.headers,
|
|
232
|
+
body: req.body || void 0
|
|
233
|
+
});
|
|
234
|
+
const body = await res.text();
|
|
235
|
+
const headers = {};
|
|
236
|
+
res.headers.forEach((v, k) => {
|
|
237
|
+
headers[k] = v;
|
|
238
|
+
});
|
|
305
239
|
writeMessage({
|
|
306
240
|
type: "proxy_response",
|
|
307
241
|
requestId: req.requestId,
|
|
308
|
-
status:
|
|
309
|
-
headers
|
|
310
|
-
body
|
|
242
|
+
status: res.status,
|
|
243
|
+
headers,
|
|
244
|
+
body
|
|
311
245
|
});
|
|
312
246
|
} catch (e) {
|
|
313
247
|
writeMessage({
|
|
@@ -317,6 +251,26 @@ async function handleProxy(req) {
|
|
|
317
251
|
});
|
|
318
252
|
}
|
|
319
253
|
}
|
|
254
|
+
function handleStartProxy(req) {
|
|
255
|
+
try {
|
|
256
|
+
startProxyServer({
|
|
257
|
+
port: req.port,
|
|
258
|
+
sessionKey: req.sessionKey,
|
|
259
|
+
providers: req.providers,
|
|
260
|
+
sendToExtension: (msg) => writeMessage(msg)
|
|
261
|
+
});
|
|
262
|
+
writeMessage({
|
|
263
|
+
type: "proxy-started",
|
|
264
|
+
port: req.port
|
|
265
|
+
});
|
|
266
|
+
} catch (e) {
|
|
267
|
+
writeMessage({
|
|
268
|
+
type: "proxy_error",
|
|
269
|
+
requestId: "start-proxy",
|
|
270
|
+
error: e.message
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
}
|
|
320
274
|
async function main() {
|
|
321
275
|
process.stdin.resume();
|
|
322
276
|
while (true) {
|
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++;
|