mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-15 06:04:40 +00:00
Proxy package
This commit is contained in:
parent
aaea0f4600
commit
66f092c0c6
9 changed files with 249 additions and 4 deletions
41
package-lock.json
generated
41
package-lock.json
generated
|
|
@ -715,6 +715,18 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@hono/node-server": {
|
||||
"version": "1.19.5",
|
||||
"resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.5.tgz",
|
||||
"integrity": "sha512-iBuhh+uaaggeAuf+TftcjZyWh2GEgZcVGXkNtskLVoWaXhnJtC5HLHrU8W1KHDoucqO1MswwglmkWLFyiDn4WQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18.14.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"hono": "^4"
|
||||
}
|
||||
},
|
||||
"node_modules/@isaacs/fs-minipass": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
|
||||
|
|
@ -836,6 +848,10 @@
|
|||
"resolved": "packages/ai",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@mariozechner/pi-proxy": {
|
||||
"resolved": "packages/proxy",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@mariozechner/pi-reader-extension": {
|
||||
"resolved": "packages/browser-extension",
|
||||
"link": true
|
||||
|
|
@ -2997,6 +3013,15 @@
|
|||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/hono": {
|
||||
"version": "4.9.10",
|
||||
"resolved": "https://registry.npmjs.org/hono/-/hono-4.9.10.tgz",
|
||||
"integrity": "sha512-AlI15ijFyKTXR7eHo7QK7OR4RoKIedZvBuRjO8iy4zrxvlY5oFCdiRG/V/lFJHCNXJ0k72ATgnyzx8Yqa5arug==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=16.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/html-parse-string": {
|
||||
"version": "0.0.9",
|
||||
"resolved": "https://registry.npmjs.org/html-parse-string/-/html-parse-string-0.0.9.tgz",
|
||||
|
|
@ -5409,6 +5434,22 @@
|
|||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"packages/proxy": {
|
||||
"name": "@mariozechner/pi-proxy",
|
||||
"version": "0.5.43",
|
||||
"dependencies": {
|
||||
"@hono/node-server": "^1.14.0",
|
||||
"hono": "^4.6.16"
|
||||
},
|
||||
"bin": {
|
||||
"pi-proxy": "dist/cli.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.10.5",
|
||||
"tsx": "^4.19.2",
|
||||
"typescript": "^5.7.3"
|
||||
}
|
||||
},
|
||||
"packages/tui": {
|
||||
"name": "@mariozechner/pi-tui",
|
||||
"version": "0.5.43",
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@
|
|||
],
|
||||
"scripts": {
|
||||
"clean": "npm run clean --workspaces",
|
||||
"build": "npm run build -w @mariozechner/pi-tui && npm run build -w @mariozechner/pi-ai && npm run build -w @mariozechner/pi-web-ui && npm run build -w @mariozechner/pi-reader-extension && npm run build -w @mariozechner/pi-agent && npm run build -w @mariozechner/pi",
|
||||
"dev": "concurrently --names \"ai,web-ui,browser-ext,tui\" --prefix-colors \"cyan,green,yellow,magenta\" \"npm run dev -w @mariozechner/pi-ai\" \"npm run dev -w @mariozechner/pi-web-ui\" \"npm run dev -w @mariozechner/pi-reader-extension\" \"npm run dev -w @mariozechner/pi-tui\"",
|
||||
"build": "npm run build -w @mariozechner/pi-tui && npm run build -w @mariozechner/pi-ai && npm run build -w @mariozechner/pi-web-ui && npm run build -w @mariozechner/pi-reader-extension && npm run build -w @mariozechner/pi-agent && npm run build -w @mariozechner/pi-proxy && npm run build -w @mariozechner/pi",
|
||||
"dev": "concurrently --names \"ai,web-ui,browser-ext,tui,proxy\" --prefix-colors \"cyan,green,yellow,magenta,blue\" \"npm run dev -w @mariozechner/pi-ai\" \"npm run dev -w @mariozechner/pi-web-ui\" \"npm run dev -w @mariozechner/pi-reader-extension\" \"npm run dev -w @mariozechner/pi-tui\" \"npm run dev -w @mariozechner/pi-proxy\"",
|
||||
"check": "biome check --write . && npm run check --workspaces && tsc --noEmit",
|
||||
"test": "npm run test --workspaces --if-present",
|
||||
"version:patch": "npm version patch -ws --no-git-tag-version && node scripts/sync-versions.js",
|
||||
|
|
|
|||
67
packages/proxy/README.md
Normal file
67
packages/proxy/README.md
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
# @mariozechner/pi-proxy
|
||||
|
||||
CORS and authentication proxy for pi-ai. Enables browser clients to access OAuth-protected endpoints.
|
||||
|
||||
## Usage
|
||||
|
||||
### CORS Proxy
|
||||
|
||||
Zero-config CORS proxy for development:
|
||||
|
||||
```bash
|
||||
# Run directly with tsx
|
||||
npx tsx packages/proxy/src/cors-proxy.ts 3001
|
||||
|
||||
# Or use npm script
|
||||
npm run dev -w @mariozechner/pi-proxy
|
||||
|
||||
# Or install globally and use CLI
|
||||
npm install -g @mariozechner/pi-proxy
|
||||
pi-proxy 3001
|
||||
```
|
||||
|
||||
The proxy will forward requests to any URL:
|
||||
|
||||
```javascript
|
||||
// Instead of:
|
||||
fetch('https://api.anthropic.com/v1/messages', { ... })
|
||||
|
||||
// Use:
|
||||
fetch('http://localhost:3001?url=https://api.anthropic.com/v1/messages', { ... })
|
||||
```
|
||||
|
||||
### OAuth Integration
|
||||
|
||||
For Anthropic OAuth tokens, configure your client to use the proxy:
|
||||
|
||||
```typescript
|
||||
import Anthropic from '@anthropic-ai/sdk';
|
||||
|
||||
const client = new Anthropic({
|
||||
apiKey: 'oauth_token_here',
|
||||
baseURL: 'http://localhost:3001?url=https://api.anthropic.com'
|
||||
});
|
||||
```
|
||||
|
||||
## Future Proxy Types
|
||||
|
||||
- **BunnyCDN Edge Function**: Deploy as edge function
|
||||
- **Managed Proxy**: Self-hosted with provider key management and credential auth
|
||||
- **Cloudflare Worker**: Deploy as CF worker
|
||||
|
||||
## Architecture
|
||||
|
||||
The proxy:
|
||||
1. Accepts requests with `?url=<target>` query parameter
|
||||
2. Forwards all headers (except `host`, `origin`)
|
||||
3. Forwards request body for non-GET/HEAD requests
|
||||
4. Returns response with CORS headers enabled
|
||||
5. Strips CORS headers from upstream response
|
||||
|
||||
## Development
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run build
|
||||
npm run check
|
||||
```
|
||||
27
packages/proxy/package.json
Normal file
27
packages/proxy/package.json
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"name": "@mariozechner/pi-proxy",
|
||||
"version": "0.5.43",
|
||||
"type": "module",
|
||||
"description": "CORS and authentication proxy for pi-ai",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"bin": {
|
||||
"pi-proxy": "dist/cli.js"
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf dist",
|
||||
"build": "tsc",
|
||||
"check": "biome check --write .",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"dev": "tsx src/cors-proxy.ts 3001"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hono/node-server": "^1.14.0",
|
||||
"hono": "^4.6.16"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.10.5",
|
||||
"tsx": "^4.19.2",
|
||||
"typescript": "^5.7.3"
|
||||
}
|
||||
}
|
||||
16
packages/proxy/src/cli.ts
Normal file
16
packages/proxy/src/cli.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#!/usr/bin/env node
|
||||
import { spawn } from "node:child_process";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const port = process.argv[2] || "3001";
|
||||
|
||||
// Run the CORS proxy
|
||||
const child = spawn("node", [path.join(__dirname, "cors-proxy.js"), port], {
|
||||
stdio: "inherit",
|
||||
});
|
||||
|
||||
child.on("exit", (code) => {
|
||||
process.exit(code || 0);
|
||||
});
|
||||
73
packages/proxy/src/cors-proxy.ts
Normal file
73
packages/proxy/src/cors-proxy.ts
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
#!/usr/bin/env node
|
||||
import { serve } from "@hono/node-server";
|
||||
import { Hono } from "hono";
|
||||
import { cors } from "hono/cors";
|
||||
|
||||
export function createCorsProxy() {
|
||||
const app = new Hono();
|
||||
|
||||
// Enable CORS for all origins
|
||||
app.use("*", cors());
|
||||
|
||||
// Proxy all requests
|
||||
app.all("*", async (c) => {
|
||||
const url = new URL(c.req.url);
|
||||
const targetUrl = url.searchParams.get("url");
|
||||
|
||||
if (!targetUrl) {
|
||||
return c.json({ error: "Missing 'url' query parameter" }, 400);
|
||||
}
|
||||
|
||||
try {
|
||||
// Forward the request
|
||||
const headers = new Headers();
|
||||
c.req.raw.headers.forEach((value, key) => {
|
||||
// Skip host and origin headers
|
||||
if (key.toLowerCase() !== "host" && key.toLowerCase() !== "origin") {
|
||||
headers.set(key, value);
|
||||
}
|
||||
});
|
||||
|
||||
const response = await fetch(targetUrl, {
|
||||
method: c.req.method,
|
||||
headers,
|
||||
body: c.req.method !== "GET" && c.req.method !== "HEAD" ? await c.req.raw.clone().arrayBuffer() : undefined,
|
||||
});
|
||||
|
||||
// Forward response headers
|
||||
const responseHeaders = new Headers();
|
||||
response.headers.forEach((value, key) => {
|
||||
// Skip CORS headers (we handle them)
|
||||
if (!key.toLowerCase().startsWith("access-control-")) {
|
||||
responseHeaders.set(key, value);
|
||||
}
|
||||
});
|
||||
|
||||
// Return proxied response
|
||||
return new Response(response.body, {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
headers: responseHeaders,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Proxy error:", error);
|
||||
return c.json({ error: error instanceof Error ? error.message : "Proxy request failed" }, 502);
|
||||
}
|
||||
});
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
// CLI entry point
|
||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
const app = createCorsProxy();
|
||||
const port = Number.parseInt(process.argv[2] || "3001", 10);
|
||||
|
||||
console.log(`🔌 CORS proxy running on http://localhost:${port}`);
|
||||
console.log(`Usage: http://localhost:${port}?url=<target-url>`);
|
||||
|
||||
serve({
|
||||
fetch: app.fetch,
|
||||
port,
|
||||
});
|
||||
}
|
||||
1
packages/proxy/src/index.ts
Normal file
1
packages/proxy/src/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export { createCorsProxy } from "./cors-proxy.js";
|
||||
8
packages/proxy/tsconfig.json
Normal file
8
packages/proxy/tsconfig.json
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"rootDir": "src"
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
}
|
||||
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
/**
|
||||
* Syncs inter-package dependency versions in the monorepo
|
||||
* Updates @mariozechner/pi-tui and @mariozechner/pi-agent versions
|
||||
* in dependent packages to match their current versions
|
||||
* Updates internal @mariozechner/* package versions in dependent packages
|
||||
* to match their current versions
|
||||
*/
|
||||
|
||||
import { readFileSync, writeFileSync } from 'fs';
|
||||
|
|
@ -15,11 +15,15 @@ const packagesDir = join(process.cwd(), 'packages');
|
|||
const tui = JSON.parse(readFileSync(join(packagesDir, 'tui/package.json'), 'utf8'));
|
||||
const agent = JSON.parse(readFileSync(join(packagesDir, 'agent/package.json'), 'utf8'));
|
||||
const pods = JSON.parse(readFileSync(join(packagesDir, 'pods/package.json'), 'utf8'));
|
||||
const webUi = JSON.parse(readFileSync(join(packagesDir, 'web-ui/package.json'), 'utf8'));
|
||||
const browserExtension = JSON.parse(readFileSync(join(packagesDir, 'browser-extension/package.json'), 'utf8'));
|
||||
|
||||
console.log('Current versions:');
|
||||
console.log(` @mariozechner/pi-tui: ${tui.version}`);
|
||||
console.log(` @mariozechner/pi-agent: ${agent.version}`);
|
||||
console.log(` @mariozechner/pi: ${pods.version}`);
|
||||
console.log(` @mariozechner/pi-web-ui: ${webUi.version}`);
|
||||
console.log(` @mariozechner/pi-reader-extension: ${browserExtension.version}`);
|
||||
|
||||
// Update agent's dependency on tui
|
||||
if (agent.dependencies['@mariozechner/pi-tui']) {
|
||||
|
|
@ -37,4 +41,12 @@ if (pods.dependencies['@mariozechner/pi-agent']) {
|
|||
console.log(`Updated pods' dependency on pi-agent: ${oldVersion} → ^${agent.version}`);
|
||||
}
|
||||
|
||||
// Update browser-extension's dependency on web-ui
|
||||
if (browserExtension.dependencies['@mariozechner/pi-web-ui']) {
|
||||
const oldVersion = browserExtension.dependencies['@mariozechner/pi-web-ui'];
|
||||
browserExtension.dependencies['@mariozechner/pi-web-ui'] = `^${webUi.version}`;
|
||||
writeFileSync(join(packagesDir, 'browser-extension/package.json'), JSON.stringify(browserExtension, null, '\t') + '\n');
|
||||
console.log(`Updated browser-extension's dependency on pi-web-ui: ${oldVersion} → ^${webUi.version}`);
|
||||
}
|
||||
|
||||
console.log('\n✅ Version sync complete!');
|
||||
Loading…
Add table
Add a link
Reference in a new issue