@mariozechner/pi-proxy 0.5.44
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/README.md +67 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +14 -0
- package/dist/cli.js.map +1 -0
- package/dist/cors-proxy.d.ts +4 -0
- package/dist/cors-proxy.d.ts.map +1 -0
- package/dist/cors-proxy.js +63 -0
- package/dist/cors-proxy.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/package.json +27 -0
- package/src/cli.ts +16 -0
- package/src/cors-proxy.ts +73 -0
- package/src/index.ts +1 -0
- package/tsconfig.json +8 -0
package/README.md
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# @mariozechner/pi-proxy
|
|
2
|
+
|
|
3
|
+
CORS and authentication proxy for pi-ai. Enables browser clients to access OAuth-protected endpoints.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
### CORS Proxy
|
|
8
|
+
|
|
9
|
+
Zero-config CORS proxy for development:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# Run directly with tsx
|
|
13
|
+
npx tsx packages/proxy/src/cors-proxy.ts 3001
|
|
14
|
+
|
|
15
|
+
# Or use npm script
|
|
16
|
+
npm run dev -w @mariozechner/pi-proxy
|
|
17
|
+
|
|
18
|
+
# Or install globally and use CLI
|
|
19
|
+
npm install -g @mariozechner/pi-proxy
|
|
20
|
+
pi-proxy 3001
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The proxy will forward requests to any URL:
|
|
24
|
+
|
|
25
|
+
```javascript
|
|
26
|
+
// Instead of:
|
|
27
|
+
fetch('https://api.anthropic.com/v1/messages', { ... })
|
|
28
|
+
|
|
29
|
+
// Use:
|
|
30
|
+
fetch('http://localhost:3001?url=https://api.anthropic.com/v1/messages', { ... })
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### OAuth Integration
|
|
34
|
+
|
|
35
|
+
For Anthropic OAuth tokens, configure your client to use the proxy:
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
import Anthropic from '@anthropic-ai/sdk';
|
|
39
|
+
|
|
40
|
+
const client = new Anthropic({
|
|
41
|
+
apiKey: 'oauth_token_here',
|
|
42
|
+
baseURL: 'http://localhost:3001?url=https://api.anthropic.com'
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Future Proxy Types
|
|
47
|
+
|
|
48
|
+
- **BunnyCDN Edge Function**: Deploy as edge function
|
|
49
|
+
- **Managed Proxy**: Self-hosted with provider key management and credential auth
|
|
50
|
+
- **Cloudflare Worker**: Deploy as CF worker
|
|
51
|
+
|
|
52
|
+
## Architecture
|
|
53
|
+
|
|
54
|
+
The proxy:
|
|
55
|
+
1. Accepts requests with `?url=<target>` query parameter
|
|
56
|
+
2. Forwards all headers (except `host`, `origin`)
|
|
57
|
+
3. Forwards request body for non-GET/HEAD requests
|
|
58
|
+
4. Returns response with CORS headers enabled
|
|
59
|
+
5. Strips CORS headers from upstream response
|
|
60
|
+
|
|
61
|
+
## Development
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npm install
|
|
65
|
+
npm run build
|
|
66
|
+
npm run check
|
|
67
|
+
```
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
const port = process.argv[2] || "3001";
|
|
7
|
+
// Run the CORS proxy
|
|
8
|
+
const child = spawn("node", [path.join(__dirname, "cors-proxy.js"), port], {
|
|
9
|
+
stdio: "inherit",
|
|
10
|
+
});
|
|
11
|
+
child.on("exit", (code) => {
|
|
12
|
+
process.exit(code || 0);
|
|
13
|
+
});
|
|
14
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC/D,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC;AAEvC,qBAAqB;AACrB,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,eAAe,CAAC,EAAE,IAAI,CAAC,EAAE;IAC1E,KAAK,EAAE,SAAS;CAChB,CAAC,CAAC;AAEH,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;IACzB,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC;AACzB,CAAC,CAAC,CAAC","sourcesContent":["#!/usr/bin/env node\nimport { spawn } from \"node:child_process\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\nconst port = process.argv[2] || \"3001\";\n\n// Run the CORS proxy\nconst child = spawn(\"node\", [path.join(__dirname, \"cors-proxy.js\"), port], {\n\tstdio: \"inherit\",\n});\n\nchild.on(\"exit\", (code) => {\n\tprocess.exit(code || 0);\n});\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cors-proxy.d.ts","sourceRoot":"","sources":["../src/cors-proxy.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,wBAAgB,eAAe,+EAqD9B"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { serve } from "@hono/node-server";
|
|
3
|
+
import { Hono } from "hono";
|
|
4
|
+
import { cors } from "hono/cors";
|
|
5
|
+
export function createCorsProxy() {
|
|
6
|
+
const app = new Hono();
|
|
7
|
+
// Enable CORS for all origins
|
|
8
|
+
app.use("*", cors());
|
|
9
|
+
// Proxy all requests
|
|
10
|
+
app.all("*", async (c) => {
|
|
11
|
+
const url = new URL(c.req.url);
|
|
12
|
+
const targetUrl = url.searchParams.get("url");
|
|
13
|
+
if (!targetUrl) {
|
|
14
|
+
return c.json({ error: "Missing 'url' query parameter" }, 400);
|
|
15
|
+
}
|
|
16
|
+
try {
|
|
17
|
+
// Forward the request
|
|
18
|
+
const headers = new Headers();
|
|
19
|
+
c.req.raw.headers.forEach((value, key) => {
|
|
20
|
+
// Skip host and origin headers
|
|
21
|
+
if (key.toLowerCase() !== "host" && key.toLowerCase() !== "origin") {
|
|
22
|
+
headers.set(key, value);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
const response = await fetch(targetUrl, {
|
|
26
|
+
method: c.req.method,
|
|
27
|
+
headers,
|
|
28
|
+
body: c.req.method !== "GET" && c.req.method !== "HEAD" ? await c.req.raw.clone().arrayBuffer() : undefined,
|
|
29
|
+
});
|
|
30
|
+
// Forward response headers
|
|
31
|
+
const responseHeaders = new Headers();
|
|
32
|
+
response.headers.forEach((value, key) => {
|
|
33
|
+
// Skip CORS headers (we handle them)
|
|
34
|
+
if (!key.toLowerCase().startsWith("access-control-")) {
|
|
35
|
+
responseHeaders.set(key, value);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
// Return proxied response
|
|
39
|
+
return new Response(response.body, {
|
|
40
|
+
status: response.status,
|
|
41
|
+
statusText: response.statusText,
|
|
42
|
+
headers: responseHeaders,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
console.error("Proxy error:", error);
|
|
47
|
+
return c.json({ error: error instanceof Error ? error.message : "Proxy request failed" }, 502);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
return app;
|
|
51
|
+
}
|
|
52
|
+
// CLI entry point
|
|
53
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
54
|
+
const app = createCorsProxy();
|
|
55
|
+
const port = Number.parseInt(process.argv[2] || "3001", 10);
|
|
56
|
+
console.log(`🔌 CORS proxy running on http://localhost:${port}`);
|
|
57
|
+
console.log(`Usage: http://localhost:${port}?url=<target-url>`);
|
|
58
|
+
serve({
|
|
59
|
+
fetch: app.fetch,
|
|
60
|
+
port,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=cors-proxy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cors-proxy.js","sourceRoot":"","sources":["../src/cors-proxy.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\nimport { serve } from \"@hono/node-server\";\nimport { Hono } from \"hono\";\nimport { cors } from \"hono/cors\";\n\nexport function createCorsProxy() {\n\tconst app = new Hono();\n\n\t// Enable CORS for all origins\n\tapp.use(\"*\", cors());\n\n\t// Proxy all requests\n\tapp.all(\"*\", async (c) => {\n\t\tconst url = new URL(c.req.url);\n\t\tconst targetUrl = url.searchParams.get(\"url\");\n\n\t\tif (!targetUrl) {\n\t\t\treturn c.json({ error: \"Missing 'url' query parameter\" }, 400);\n\t\t}\n\n\t\ttry {\n\t\t\t// Forward the request\n\t\t\tconst headers = new Headers();\n\t\t\tc.req.raw.headers.forEach((value, key) => {\n\t\t\t\t// Skip host and origin headers\n\t\t\t\tif (key.toLowerCase() !== \"host\" && key.toLowerCase() !== \"origin\") {\n\t\t\t\t\theaders.set(key, value);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tconst response = await fetch(targetUrl, {\n\t\t\t\tmethod: c.req.method,\n\t\t\t\theaders,\n\t\t\t\tbody: c.req.method !== \"GET\" && c.req.method !== \"HEAD\" ? await c.req.raw.clone().arrayBuffer() : undefined,\n\t\t\t});\n\n\t\t\t// Forward response headers\n\t\t\tconst responseHeaders = new Headers();\n\t\t\tresponse.headers.forEach((value, key) => {\n\t\t\t\t// Skip CORS headers (we handle them)\n\t\t\t\tif (!key.toLowerCase().startsWith(\"access-control-\")) {\n\t\t\t\t\tresponseHeaders.set(key, value);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// Return proxied response\n\t\t\treturn new Response(response.body, {\n\t\t\t\tstatus: response.status,\n\t\t\t\tstatusText: response.statusText,\n\t\t\t\theaders: responseHeaders,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Proxy error:\", error);\n\t\t\treturn c.json({ error: error instanceof Error ? error.message : \"Proxy request failed\" }, 502);\n\t\t}\n\t});\n\n\treturn app;\n}\n\n// CLI entry point\nif (import.meta.url === `file://${process.argv[1]}`) {\n\tconst app = createCorsProxy();\n\tconst port = Number.parseInt(process.argv[2] || \"3001\", 10);\n\n\tconsole.log(`🔌 CORS proxy running on http://localhost:${port}`);\n\tconsole.log(`Usage: http://localhost:${port}?url=<target-url>`);\n\n\tserve({\n\t\tfetch: app.fetch,\n\t\tport,\n\t});\n}\n"]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC","sourcesContent":["export { createCorsProxy } from \"./cors-proxy.js\";\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mariozechner/pi-proxy",
|
|
3
|
+
"version": "0.5.44",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "CORS and authentication proxy for pi-ai",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"pi-proxy": "dist/cli.js"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"clean": "rm -rf dist",
|
|
13
|
+
"build": "tsc",
|
|
14
|
+
"check": "biome check --write .",
|
|
15
|
+
"typecheck": "tsc --noEmit",
|
|
16
|
+
"dev": "tsx src/cors-proxy.ts 3001"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@hono/node-server": "^1.14.0",
|
|
20
|
+
"hono": "^4.6.16"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@types/node": "^22.10.5",
|
|
24
|
+
"tsx": "^4.19.2",
|
|
25
|
+
"typescript": "^5.7.3"
|
|
26
|
+
}
|
|
27
|
+
}
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
|
|
6
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const port = process.argv[2] || "3001";
|
|
8
|
+
|
|
9
|
+
// Run the CORS proxy
|
|
10
|
+
const child = spawn("node", [path.join(__dirname, "cors-proxy.js"), port], {
|
|
11
|
+
stdio: "inherit",
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
child.on("exit", (code) => {
|
|
15
|
+
process.exit(code || 0);
|
|
16
|
+
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { serve } from "@hono/node-server";
|
|
3
|
+
import { Hono } from "hono";
|
|
4
|
+
import { cors } from "hono/cors";
|
|
5
|
+
|
|
6
|
+
export function createCorsProxy() {
|
|
7
|
+
const app = new Hono();
|
|
8
|
+
|
|
9
|
+
// Enable CORS for all origins
|
|
10
|
+
app.use("*", cors());
|
|
11
|
+
|
|
12
|
+
// Proxy all requests
|
|
13
|
+
app.all("*", async (c) => {
|
|
14
|
+
const url = new URL(c.req.url);
|
|
15
|
+
const targetUrl = url.searchParams.get("url");
|
|
16
|
+
|
|
17
|
+
if (!targetUrl) {
|
|
18
|
+
return c.json({ error: "Missing 'url' query parameter" }, 400);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
// Forward the request
|
|
23
|
+
const headers = new Headers();
|
|
24
|
+
c.req.raw.headers.forEach((value, key) => {
|
|
25
|
+
// Skip host and origin headers
|
|
26
|
+
if (key.toLowerCase() !== "host" && key.toLowerCase() !== "origin") {
|
|
27
|
+
headers.set(key, value);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const response = await fetch(targetUrl, {
|
|
32
|
+
method: c.req.method,
|
|
33
|
+
headers,
|
|
34
|
+
body: c.req.method !== "GET" && c.req.method !== "HEAD" ? await c.req.raw.clone().arrayBuffer() : undefined,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Forward response headers
|
|
38
|
+
const responseHeaders = new Headers();
|
|
39
|
+
response.headers.forEach((value, key) => {
|
|
40
|
+
// Skip CORS headers (we handle them)
|
|
41
|
+
if (!key.toLowerCase().startsWith("access-control-")) {
|
|
42
|
+
responseHeaders.set(key, value);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Return proxied response
|
|
47
|
+
return new Response(response.body, {
|
|
48
|
+
status: response.status,
|
|
49
|
+
statusText: response.statusText,
|
|
50
|
+
headers: responseHeaders,
|
|
51
|
+
});
|
|
52
|
+
} catch (error) {
|
|
53
|
+
console.error("Proxy error:", error);
|
|
54
|
+
return c.json({ error: error instanceof Error ? error.message : "Proxy request failed" }, 502);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
return app;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// CLI entry point
|
|
62
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
63
|
+
const app = createCorsProxy();
|
|
64
|
+
const port = Number.parseInt(process.argv[2] || "3001", 10);
|
|
65
|
+
|
|
66
|
+
console.log(`🔌 CORS proxy running on http://localhost:${port}`);
|
|
67
|
+
console.log(`Usage: http://localhost:${port}?url=<target-url>`);
|
|
68
|
+
|
|
69
|
+
serve({
|
|
70
|
+
fetch: app.fetch,
|
|
71
|
+
port,
|
|
72
|
+
});
|
|
73
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createCorsProxy } from "./cors-proxy.js";
|