tavern-harness 0.1.11 → 0.1.12
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 +6 -2
- package/README.zh.md +4 -0
- package/package.json +1 -1
- package/src/core/proxy.ts +8 -7
- package/vite.config.ts +11 -6
package/README.md
CHANGED
|
@@ -10,6 +10,10 @@
|
|
|
10
10
|
<a href="https://www.npmjs.com/package/tavern-harness"><img alt="npm" src="https://img.shields.io/npm/v/tavern-harness?style=flat-square" /></a>
|
|
11
11
|
</p>
|
|
12
12
|
|
|
13
|
+
<p align="center">
|
|
14
|
+
<strong>English</strong> | <a href="README.zh.md">简体中文</a>
|
|
15
|
+
</p>
|
|
16
|
+
|
|
13
17
|
# Tavern Harness · An AI-Native Role-Playing Sandbox
|
|
14
18
|
|
|
15
19
|
<a id="demo"></a>
|
|
@@ -74,9 +78,9 @@ After starting the app, open **Settings → Model Service Provider**:
|
|
|
74
78
|
2. **Test the connection**: the app fetches the model list automatically. If the target service does not expose CORS headers, requests are forwarded through the built-in same-origin proxy for private-network or LAN addresses. On success, the Base URL is rewritten to its proxy form and saved.
|
|
75
79
|
3. **Select a model**: click the model name at the top of the chat page, then return to the conversation and start chatting.
|
|
76
80
|
|
|
77
|
-
### Import a
|
|
81
|
+
### Import a Gameplay and Start Playing
|
|
78
82
|
|
|
79
|
-
Download the
|
|
83
|
+
Download the gameplay file below. Click **Import** in the upper-left corner, then import the file you downloaded to create a complete game environment—including characters, lorebooks, skills, and more—and start playing right away:
|
|
80
84
|
|
|
81
85
|
[th-demo-affinity-gameplay-en.json](./docs/assets/gameplay/th-demo-affinity-gameplay-en.json)
|
|
82
86
|
|
package/README.zh.md
CHANGED
|
@@ -10,6 +10,10 @@
|
|
|
10
10
|
<a href="https://www.npmjs.com/package/tavern-harness"><img alt="npm" src="https://img.shields.io/npm/v/tavern-harness?style=flat-square" /></a>
|
|
11
11
|
</p>
|
|
12
12
|
|
|
13
|
+
<p align="center">
|
|
14
|
+
<a href="README.md">English</a> | <strong>简体中文</strong>
|
|
15
|
+
</p>
|
|
16
|
+
|
|
13
17
|
# Tavern Harness · AI 原生的角色扮演沙盒
|
|
14
18
|
|
|
15
19
|
<a id="demo"></a>
|
package/package.json
CHANGED
package/src/core/proxy.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// 自动回退到 Vite 开发服务器的同源代理。
|
|
4
4
|
// 代理规则:http://<当前站点>/api/<host:port>/<剩余路径>
|
|
5
5
|
// 例:http://192.168.1.5:8788/v1/ → http://localhost:5173/api/192.168.1.5:8788/v1/
|
|
6
|
+
// https://api.example.com/v1/ → http://localhost:5173/api/https/api.example.com/v1/
|
|
6
7
|
// ============================================================
|
|
7
8
|
|
|
8
9
|
/** 判断当前环境是否可能提供代理(Vite dev 服务器) */
|
|
@@ -29,7 +30,7 @@ export function isProxyUrl(url: string): boolean {
|
|
|
29
30
|
* 例:
|
|
30
31
|
* http://192.168.1.5:8788 → http://localhost:5173/api/192.168.1.5:8788
|
|
31
32
|
* http://192.168.1.5:8788/v1/ → http://localhost:5173/api/192.168.1.5:8788/v1/
|
|
32
|
-
* https://api.deepseek.com/v1/ →
|
|
33
|
+
* https://api.deepseek.com/v1/ → http://localhost:5173/api/https/api.deepseek.com/v1/
|
|
33
34
|
*/
|
|
34
35
|
export function toProxyUrl(baseUrl: string): string | null {
|
|
35
36
|
if (!canUseProxy() || isProxyUrl(baseUrl)) return null;
|
|
@@ -39,15 +40,15 @@ export function toProxyUrl(baseUrl: string): string | null {
|
|
|
39
40
|
} catch {
|
|
40
41
|
return null;
|
|
41
42
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
if (!isPrivateHost) return null;
|
|
43
|
+
|
|
44
|
+
const isLocalHost = u.hostname === 'localhost' || u.hostname === '127.0.0.1';
|
|
45
|
+
const isIpv4 = /^\d{1,3}(\.\d{1,3}){3}$/.test(u.hostname);
|
|
46
|
+
if (u.protocol !== 'https:' && !isLocalHost && !isIpv4) return null;
|
|
47
47
|
|
|
48
48
|
const host = u.host; // hostname:port
|
|
49
49
|
const rest = u.pathname.replace(/^\/+/, '');
|
|
50
|
-
const
|
|
50
|
+
const protocol = u.protocol === 'https:' ? 'https/' : '';
|
|
51
|
+
const path = `/api/${protocol}${host}/${rest}`.replace(/\/+$/, '');
|
|
51
52
|
return `${window.location.origin}${path}`;
|
|
52
53
|
}
|
|
53
54
|
|
package/vite.config.ts
CHANGED
|
@@ -13,9 +13,11 @@ import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
|
13
13
|
const projectRoot = dirname(fileURLToPath(import.meta.url));
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
|
-
* CORS
|
|
16
|
+
* CORS 转发中间件:为本地及外部 HTTPS OpenAI 兼容服务提供同源代理。
|
|
17
17
|
* Base URL 填 http://localhost:5173/api/192.168.50.175:8788/ 即可;
|
|
18
|
-
* /api/<host
|
|
18
|
+
* HTTPS 目标格式为 /api/https/<host>/,例如:
|
|
19
|
+
* http://localhost:5173/api/https/ark.cn-beijing.volces.com/api/v3。
|
|
20
|
+
* /api/<host>/ 之后的路径(含 query)原样转发到目标服务。
|
|
19
21
|
*/
|
|
20
22
|
function corsProxyPlugin(): Plugin {
|
|
21
23
|
return {
|
|
@@ -26,10 +28,13 @@ function corsProxyPlugin(): Plugin {
|
|
|
26
28
|
const m = /^\/api\/([^/]+)(\/[^?]*)?(\?.*)?$/.exec(url);
|
|
27
29
|
if (!m) return next();
|
|
28
30
|
|
|
29
|
-
const rawHost = m[1]; // e.g. 192.168.50.175:8788
|
|
31
|
+
const rawHost = m[1]; // e.g. 192.168.50.175:8788 or https
|
|
30
32
|
const path = (m[2] || '') + (m[3] || '');
|
|
31
|
-
const isHttps = rawHost
|
|
32
|
-
const host =
|
|
33
|
+
const isHttps = rawHost === 'https';
|
|
34
|
+
const host = isHttps ? (m[2] || '').split('/')[1] : rawHost;
|
|
35
|
+
const forwardedPath = isHttps
|
|
36
|
+
? `/${(m[2] || '').split('/').slice(2).join('/')}${m[3] || ''}`
|
|
37
|
+
: path;
|
|
33
38
|
const [hostname, portStr] = host.split(':');
|
|
34
39
|
const port = portStr ? parseInt(portStr, 10) : isHttps ? 443 : 80;
|
|
35
40
|
|
|
@@ -44,7 +49,7 @@ function corsProxyPlugin(): Plugin {
|
|
|
44
49
|
protocol: isHttps ? 'https:' : 'http:',
|
|
45
50
|
hostname,
|
|
46
51
|
port,
|
|
47
|
-
path,
|
|
52
|
+
path: forwardedPath,
|
|
48
53
|
method: req.method,
|
|
49
54
|
headers: forwardedHeaders,
|
|
50
55
|
},
|