tavern-harness 0.1.4 → 0.1.6

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 CHANGED
@@ -1,12 +1,10 @@
1
1
  # Tavern Harness · An AI-Native Role-Playing Sandbox
2
2
 
3
- [GitHub Repository](https://github.com/Rannichan/Tavern-Harness) · [Product Site](https://rannichan.github.io/Tavern-Harness/) · [Open Sandbox](https://rannichan.github.io/Tavern-Harness/#world) · [Report an Issue](https://github.com/Rannichan/Tavern-Harness/issues) · [Discussions](https://github.com/Rannichan/Tavern-Harness/discussions) · [Discord](https://discord.gg/kPSWGeaHx) · [Skill Sandbox Design](./notebook/skill-sandbox-design.md)
3
+ [Product Site](https://rannichan.github.io/Tavern-Harness/) · [Wiki](https://rannichan.github.io/Tavern-Harness/wiki.html) · [GitHub Repository](https://github.com/Rannichan/Tavern-Harness) · [Discord](https://discord.gg/kPSWGeaHx)
4
4
 
5
5
  <a id="run"></a>
6
6
  ## 1 · Getting Started
7
7
 
8
- > Requires **Node.js 18 or later**. Tavern Harness is a frontend web app with an optional local sandbox service—no Docker or database server required.
9
-
10
8
  ### Option 1: One-Command Start (Recommended)
11
9
 
12
10
  No repository clone or global installation is required:
@@ -22,9 +20,28 @@ npx tavern-harness --no-open
22
20
  npx tavern-harness --port 5273
23
21
  ```
24
22
 
25
- The service listens only on the local machine. When using shell skills, commands run locally with the permissions of the user who started the command. Run only versions from trusted sources and review every command-confirmation dialog.
23
+ ### Option 2: Global Installation (Recommended for Regular Use)
24
+
25
+ Install once, then start Tavern Harness from any directory:
26
+
27
+ ```bash
28
+ npm install -g tavern-harness
29
+ tavern-harness
30
+ ```
31
+
32
+ To update it:
33
+
34
+ ```bash
35
+ npm update -g tavern-harness
36
+ ```
37
+
38
+ To uninstall it:
26
39
 
27
- ### Option 2: Develop from Source
40
+ ```bash
41
+ npm uninstall -g tavern-harness
42
+ ```
43
+
44
+ ### Option 3: Build from Source
28
45
 
29
46
  ```bash
30
47
  git clone https://github.com/Rannichan/Tavern-Harness.git
@@ -82,50 +99,7 @@ The project has no external harness dependency, and its UI is designed for a cle
82
99
  <a id="gameplay"></a>
83
100
  ## 3 · Gameplay Examples
84
101
 
85
- > These examples progress from simple to advanced: the first three work out of the box, while the remaining four gradually introduce sandbox capabilities. In every example, “the Keeper” means the built-in **Tavern Keeper** character, which exists on first launch and has all built-in skills enabled by default.
86
-
87
- ### Example 1: Late-Night Company (No Setup)
88
-
89
- 1. Create a new conversation → select **NPC Chat** → choose **Tavern Keeper** or any custom character.
90
- 2. Chat naturally as you would with a friend. Enable **thinking mode** to see the Keeper's reasoning.
91
- 3. Long-press or right-click any message to edit history, regenerate, inspect raw logs, or fork a branch.
92
-
93
- ### Example 2: Tabletop Dice Rolls (Role-Playing)
94
-
95
- 1. Create an **NPC Chat** for a GM and give the character a prompt such as: “You are the GM for this campaign; use `roll_dice` to decide fate.”
96
- 2. Use the opening message to introduce a story hook. The Keeper can call `roll_dice` and display results such as critical successes or failures.
97
- 3. Add campaign rules and world setting to a Lorebook, then bind it to the conversation to turn the character into a complete world.
98
-
99
- ### Example 3: A Group-Chat Script (Up to Six Characters)
100
-
101
- 1. Create multiple NPCs in the **Character Workshop**, such as a detective, butler, and protagonist.
102
- 2. Create a **Group Chat**, add up to six characters, drag to set their speaking order, or enable random ordering.
103
- 3. Use `@character-name` to choose the next speaker. Enter `/new` to begin a new topic with truncated context, or `/pass` to skip your turn.
104
-
105
- ### Example 4: Create Skills in the Moment
106
-
107
- 1. Ask the Keeper: “Create a skill that accepts a city and returns the weather.” It can call `create_skill` to create a new `template` or `http_get` skill.
108
- 2. New skills are disabled for all characters by default. Enable them for the desired character in **Character Workshop → Skills**.
109
- 3. Skills can use `$read`, `$write`, `$append`, and `$list` to work with the conversation workspace, with no restart required.
110
-
111
- ### Example 5: A Persistent Mini-Game (Advanced Sandbox)
112
-
113
- 1. Ask the Keeper to write a `javascript` skill for an affinity system, saving each turn's value to `state.json` with `$write`.
114
- 2. Create another skill that reads with `$read` and uses `file_write` to generate an HTML dashboard, `dashboard.html`.
115
- 3. Use `file_display` to visualize value changes in a pop-up; workspace files can be previewed in an isolated, resizable HTML view.
116
- 4. Deleting the conversation also removes its `sandbox_workspace/session-<id>/` directory.
117
-
118
- ### Example 6: Organize Files with Shell Skills
119
-
120
- 1. Ask the Keeper in chat to use `ls` to inspect the workspace, `cat` to read files, and `cp` to archive creative work.
121
- 2. Reads and writes must remain in the current conversation workspace. Non-allowlisted commands such as `node` and `git`, plus modifying operations such as `mv` and `rm`, require confirmation.
122
- 3. Regular conversations have read-only access to `public/`. Only the built-in Tavern Keeper can write to the public area, allowing shared assets across conversations.
123
-
124
- ### Example 7: Take an Entire World with You (Import, Export, and Fork)
125
-
126
- 1. Mid-game, choose **Export Game** and decide whether to include conversation history to create a self-contained JSON file.
127
- 2. Send the JSON to a friend, who can use **Import Game** to restore your characters and world in one step. Conflicts create copies instead of overwriting data.
128
- 3. Or create a branch / fork to explore multiple endings from the same history.
102
+ See the [interactive gameplay demos](https://rannichan.github.io/Tavern-Harness/#demo).
129
103
 
130
104
  <a id="feedback"></a>
131
105
  ## 4 · Community and Feedback
package/README.zh.md CHANGED
@@ -1,12 +1,10 @@
1
1
  # Tavern Harness · AI 原生的角色扮演沙盒
2
2
 
3
- [GitHub 仓库](https://github.com/Rannichan/Tavern-Harness) · [产品主页](https://rannichan.github.io/Tavern-Harness/) · [高自由度沙盒](https://rannichan.github.io/Tavern-Harness/#world) · [问题反馈](https://github.com/Rannichan/Tavern-Harness/issues) · [讨论区](https://github.com/Rannichan/Tavern-Harness/discussions) · [Discord 交流](https://discord.gg/kPSWGeaHx) · [技能沙箱设计文档](./notebook/skill-sandbox-design.md)
3
+ [产品主页](https://rannichan.github.io/Tavern-Harness/) · [Wiki](https://rannichan.github.io/Tavern-Harness/wiki.html) · [GitHub 仓库](https://github.com/Rannichan/Tavern-Harness) · [Discord 交流](https://discord.gg/kPSWGeaHx)
4
4
 
5
5
  <a id="run"></a>
6
6
  ## 1 · 如何运行
7
7
 
8
- > 需要本机装有 **Node.js ≥ 18**。Tavern Harness 是一个纯前端 Web 应用 + 可选的本地沙箱服务,无需 Docker、无需数据库服务。
9
-
10
8
  ### 方式一:一键启动(推荐)
11
9
 
12
10
  无需克隆仓库或全局安装:
@@ -22,9 +20,29 @@ npx tavern-harness --no-open
22
20
  npx tavern-harness --port 5273
23
21
  ```
24
22
 
25
- 服务仅监听本机。使用 shell 技能时,命令会以启动命令的当前用户权限在本机执行;请仅运行来自可信来源的版本,并审阅每次命令确认弹窗。
26
23
 
27
- ### 方式二:从源码开发
24
+ ### 方式二:全局安装(推荐长期使用)
25
+
26
+ 安装一次后,可从任意目录启动 Tavern Harness:
27
+
28
+ ```bash
29
+ npm install -g tavern-harness
30
+ tavern-harness
31
+ ```
32
+
33
+ 更新:
34
+
35
+ ```bash
36
+ npm update -g tavern-harness
37
+ ```
38
+
39
+ 卸载:
40
+
41
+ ```bash
42
+ npm uninstall -g tavern-harness
43
+ ```
44
+
45
+ ### 方式三:从源码安装
28
46
 
29
47
  ```bash
30
48
  git clone https://github.com/Rannichan/Tavern-Harness.git
@@ -82,50 +100,7 @@ npm run dev # 开发服务器默认端口 5173,被占用时自动顺延
82
100
  <a id="gameplay"></a>
83
101
  ## 3 · 玩法示例
84
102
 
85
- > 「玩法示例」由浅入深:前三例零配置开箱即玩,后四例逐步解锁沙盒能力。所有例子中的「老板」=内置角色「酒馆老板」,首次启动即存在,默认启用全部内置技能。
86
-
87
- ### 示例一:深夜陪伴(零门槛)
88
-
89
- 1. 新建对话 → 选「NPC 对话」→ 角色「酒馆老板」(或任意自定义角色)
90
- 2. 直接开聊,像跟朋友一样对话;开启**思考模式**还能看到老板的内心盘算
91
- 3. 长按 / 右键任意消息:编辑历史、重新生成、查看原始日志、Fork 分支
92
-
93
- ### 示例二:跑团掷骰(角色扮演)
94
-
95
- 1. 新建「NPC 对话」,角色当 GM:人设写上「你是主持本团的 GM,用 roll_dice 决定命运」
96
- 2. 开场白抛出剧情钩子;老板会自动调用 `roll_dice`,并在消息里呈现「大成功!/ 大失败!」
97
- 3. 把团规、世界观写进 Lorebook 绑定到该会话,人设即刻变成一个完整世界
98
-
99
- ### 示例三:群聊剧本(最多 6 人一台戏)
100
-
101
- 1. 「角色工坊」里建多个 NPC(如侦探、管家、女主)
102
- 2. 新建「群聊」对话,添加至多 6 个角色:拖拽设定发言顺序,或开启随机洗牌
103
- 3. 用 `@角色名` 点名下一个发言者;输入 `/new` 开新话题(截断上下文);输入 `/pass` 跳过自己本轮发言
104
-
105
- ### 示例四:现场造技能(自造能力)
106
-
107
- 1. 对老板说:「帮我生成一个技能,输入城市名返回天气」,它会调用 `create_skill` 写出 `template` / `http_get` 实现的新技能
108
- 2. 新技能默认未启用于任何角色,去「角色工坊 → 技能表」为要用的角色勾选生效
109
- 3. 技能支持 `$read` / `$write` / `$append` / `$list` 桥接会话工作区,无需重启即时生效
110
-
111
- ### 示例五:持久化小游戏(沙盒高阶)
112
-
113
- 1. 让老板写一个 `javascript` 技能实现好感度系统:每轮数值经 `$write` 存进工作目录 `state.json`
114
- 2. 再写一个读取 `$read` 的技能,用 `file_write` 生成 HTML 仪表盘 `dashboard.html`
115
- 3. `file_display` 直接弹窗图形化展示数值变化(HTML 沙箱渲染、可缩放、可画中画),工作区内文件均可预览
116
- 4. 删除会话时磁盘目录 `sandbox_workspace/session-<id>/` 自动一并清理
117
-
118
- ### 示例六:整理文件(shell)
119
-
120
- 1. 对话里让老板 `ls` 看工作区、`cat` 读文件、`cp` 归档你的创作文稿
121
- 2. 读写必须留在本会话工作目录内;白名单外命令(如 `node`、`git`)与 `mv` / `rm` 类修改操作都会先弹窗确认再执行
122
- 3. 普通会话对公共目录 `public/` 只读——只有内置「酒馆老板」的会话能写入公共区,供所有会话共享素材
123
-
124
- ### 示例七:携带整个世界(导入导出 / Fork)
125
-
126
- 1. 一局游戏玩到中期,「导出游戏」勾选是否包含对话历史,得到一个自包含 JSON
127
- 2. 把 JSON 发给朋友,对方「导入游戏」一键还原你的角色与世界(冲突自动建副本不覆盖)
128
- 3. 或自己「创建分支 / Fork」:同一段历史分岔出多个平行结局
103
+ 请查看[互动玩法演示](https://rannichan.github.io/Tavern-Harness/#demo)。
129
104
 
130
105
  <a id="feedback"></a>
131
106
  ## 4 · 交流反馈
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tavern-harness",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Tavern Harness — 酒馆模式下的本地 AI Agent 助手(MyAgent-Android 的 Web 复刻)",
5
5
  "private": false,
6
6
  "type": "module",
@@ -44,9 +44,7 @@ export function buildGroupSystemPrompt(
44
44
  ? `The other participants are: ${others.join(', ')}.\nThe user (a participant named "${playerName}") may speak at any moment — when they do, respond naturally as ${activeSpeakerName}.\n`
45
45
  : '') +
46
46
  `Messages prefixed with [${translate('prompt.roleTag')}] are utterances by that participant; messages prefixed with [${playerName}] are the user's.\n` +
47
- (requestPayload?.mentionOnlyMode
48
- ? `End your reply by mentioning exactly one next participant with @name. If your reply already mentions a participant, do not add another mention.\n`
49
- : `When you need to direct the next speaker, you may use @${translate('prompt.roleTag')} in your reply to call on another participant.\n`) +
47
+ `When you need to direct the next speaker, you may use @${translate('prompt.roleTag')} in your reply to call on another participant.\n` +
50
48
  `\n=== ${activeSpeakerName} ===\n` +
51
49
  activeNpcPrompt +
52
50
  (worldBookContent ? `\n\n=== ${translate('prompt.worldBook')} ===\n${worldBookContent}` : '') +
package/vite.config.ts CHANGED
@@ -6,9 +6,12 @@ import net from 'node:net';
6
6
  import { randomBytes } from 'node:crypto';
7
7
  import { spawn, type ChildProcess } from 'node:child_process';
8
8
  import { readFileSync, existsSync } from 'node:fs';
9
- import { join } from 'node:path';
9
+ import { dirname, join } from 'node:path';
10
+ import { fileURLToPath } from 'node:url';
10
11
  import type { IncomingMessage, ServerResponse } from 'node:http';
11
12
 
13
+ const projectRoot = dirname(fileURLToPath(import.meta.url));
14
+
12
15
  /**
13
16
  * CORS 转发中间件:为本地(无 CORS 头)的 OpenAI 兼容服务提供同源代理。
14
17
  * Base URL 填 http://localhost:5173/api/192.168.50.175:8788/ 即可;
@@ -71,8 +74,8 @@ function sandboxProxyPlugin(): Plugin {
71
74
  let childPort: number | null = null;
72
75
  const serviceToken = randomBytes(32).toString('base64url');
73
76
  const approvalToken = randomBytes(32).toString('base64url');
74
- const lockPath = join(process.cwd(), '.sandbox-port');
75
- const SANDBOX_SCRIPT = join(process.cwd(), 'sandbox-server.mjs');
77
+ const lockPath = join(projectRoot, '.sandbox-port');
78
+ const SANDBOX_SCRIPT = join(projectRoot, 'sandbox-server.mjs');
76
79
  const DEFAULT_PORT = 17891;
77
80
 
78
81
  const loadPort = () => {
@@ -160,7 +163,7 @@ function sandboxProxyPlugin(): Plugin {
160
163
  if (availablePort == null) return null;
161
164
  childPort = availablePort;
162
165
  const child = spawn(process.execPath, [SANDBOX_SCRIPT, String(availablePort)], {
163
- cwd: process.cwd(),
166
+ cwd: projectRoot,
164
167
  stdio: 'ignore',
165
168
  windowsHide: true,
166
169
  env: {