@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 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,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -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
@@ -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,4 @@
1
+ #!/usr/bin/env node
2
+ import { Hono } from "hono";
3
+ export declare function createCorsProxy(): Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
4
+ //# sourceMappingURL=cors-proxy.d.ts.map
@@ -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"]}
@@ -0,0 +1,2 @@
1
+ export { createCorsProxy } from "./cors-proxy.js";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -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,2 @@
1
+ export { createCorsProxy } from "./cors-proxy.js";
2
+ //# sourceMappingURL=index.js.map
@@ -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";
package/tsconfig.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "dist",
5
+ "rootDir": "src"
6
+ },
7
+ "include": ["src/**/*"]
8
+ }