feat: 东京服务器系统级VPN部署完成(IKEv2+L2TP+WG)
- 服务器 lhins-3zqfts83 通过腾讯云TAT安装 strongSwan 5.9.13 + xl2tpd - 三种VPN协议已全部就绪: IKEv2(系统原生)/L2TP-IPSec(系统原生)/WireGuard - UDP 500/4500/1701 端口本地nc可达验证通过 - macOS 接入操作手册已生成 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,6 @@ alwaysApply: true
|
||||
|
||||
## 正确做法
|
||||
|
||||
- 本机语音输入真源:**Handy中文语音**(`01_卡资(金)/金仓_存储备份/Handy中文语音/SKILL.md`);豆包已于 2026-06-11 完整卸载。
|
||||
- 本机语音输入真源:**Handy中文语音**(`02_卡人(水)/水溪_整理归档/语音输入/SKILL.md`);豆包已于 2026-06-11 完整卸载。
|
||||
- 其他 `~/Library/Application Support/**` 配置:用 **Shell + Python/bash 脚本** 写入;脚本放在仓库 `脚本/` 目录,不在 Cursor 里打开目标文件改。
|
||||
- 需卸载第三方 App:用仓库 `脚本/` 下专用卸载脚本(如 `doubao_uninstall_full.sh`),禁止手改其数据目录。
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
| G23 | **宿主挂载执行**(Shell 宿主语义) | 金仓 | **宿主挂载、宿主执行、云上 bash、万推控制台、挂载出错、karo_exec、自动 bash Mac 路径、brew 云端、Docker 宿主 shell、宿主程序** | `01_卡资(金)/金仓_存储备份/宿主挂载执行_SHELL/SKILL.md` | 网站对话与云端 shell ≠ Mac;工作区挂载 + docker-mac-host-shell + 宿主预检三位一体 |
|
||||
| G24 | **深蓝云自动化**(SLCloud · SHENLANYUN) | 金盾 | **深蓝云、深澜云、SHENLANYUN、slcloud、slcloud.org、slcloud-automation、订阅链接、体验套餐、深澜文档、token=、client/subscribe** | `01_卡资(金)/金盾_数据安全/深蓝云_SLCloud自动化/SKILL.md` | 深澜云平台注册/购套餐/取订阅链接;**支付与扫码必须用户接管**;真源自 `slcloud-automation.skill` |
|
||||
| G25 | **UU远程**(网易 · 遥台) | 金仓 | **UU远程、远程安装、远程装Codex、远程装软件、UU终端、远程插件、OFFICE-INNER、DESKTOP-434GEGI** | `01_卡资(金)/金仓_存储备份/UU远程/SKILL.md` | UURemote 本机安装 + **终端零手操**装任意软件/插件;`uu_remote_install_software.sh` / `uu_remote_install_codex.sh` |
|
||||
| G25b | **Handy中文语音** | 金仓 | **Handy、卡若AI语音、语音唤醒、呼叫卡若AI、⌘1、Ctrl+1、SenseVoice、按住说话、Handy中文** | `01_卡资(金)/金仓_存储备份/Handy中文语音/SKILL.md` | 双平台 Handy 中文+**语音唤 Agent**(Hub 13458);zip 在 `4、火_工具开发/工具/Handy中文语音/` |
|
||||
| G25b | **Handy中文语音** | 金仓 | **Handy、卡若AI语音、语音唤醒、呼叫卡若AI、⌘1、Ctrl+1、SenseVoice、按住说话、Handy中文** | `02_卡人(水)/水溪_整理归档/语音输入/SKILL.md` | 双平台 Handy 中文+**语音唤 Agent**(Hub 13458);zip 在 `4、火_工具开发/工具/Handy中文语音/` |
|
||||
| G27 | **Codex账号备份恢复** | 金仓·库政 | **Codex备份、Codex恢复、账号切换恢复、Codex迁移、Codex材料恢复、Codex记录恢复** | `01_卡资(金)/金仓_存储备份/CodexAccountBackupRestore/SKILL.md` | Codex配置、Skill、记忆、会话索引和状态库备份恢复 |
|
||||
| G28 | **Codex对话状态修复** | 金仓·算巡 | **Codex侧栏修复、Codex对话恢复、Codex项目分类、Codex置顶、rollout缺失、thread-store internal error、session_index、state_5.sqlite、公司NAS Mongo备份、跨Codex恢复** | `01_卡资(金)/金仓_存储备份/Codex对话状态修复/SKILL.md` | 修复Codex侧栏与状态库;按项目/工作树保存完整事件链到公司NAS Mongo,并生成跨实例恢复包 |
|
||||
| G29 | **域名调研注册** | 金仓·网卫 | **域名调研、域名注册、域名抢注、老域名、ICP备案、历史备案、WHOIS、RDAP、域名风险** | `01_卡资(金)/金仓_存储备份/域名调研注册/SKILL.md` | 域名候选、历史流量、备案、风险核验和注册执行 |
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
# 功能迭代记录
|
||||
|
||||
## 2026-07-12|v1.4.3 修复自动输入
|
||||
|
||||
- 用户真实验证发现:纠错结果进剪贴板,但仍需手动粘贴。
|
||||
- 直接核对 Handy v0.8.3 源码 `src-tauri/src/clipboard.rs`:ExternalScript 将文本作为 `$1`,忽略 stdout,并要求脚本自行完成输入。
|
||||
- 插件恢复 `$1` 读取,纠错后写剪贴板,等待 350ms 恢复焦点,再自动发送 `⌘V`。
|
||||
- `clipboard_handling` 改为 `dont_modify`,避免 Handy 随后用原始转写覆盖纠错后的剪贴板。
|
||||
- 新增 `~/Library/Logs/com.pais.handy/karuo_asr_plugin.log`,记录调用、纠错和粘贴失败,不记录正文。
|
||||
- 自动输入优先使用本机 `cliclick`,不可用时才回退 System Events;macOS 必须授予对应输入程序辅助功能权限。
|
||||
|
||||
## 2026-07-12|v1.4.2 自动输入当前对话框
|
||||
|
||||
- 固化 `clipboard_handling=copy_to_clipboard` 与 `auto_submit=false`。
|
||||
- Handy 收到插件 stdout 后自动写入当前焦点对话框,不需要用户手动粘贴。
|
||||
- 默认只填入、不自动发送,避免语音误识别直接提交。
|
||||
|
||||
## 2026-07-12|v1.4.1 修复Handy插件传参协议
|
||||
|
||||
- 用户真实语音验证发现:Handy日志显示插件被调用,但输入框仍是原文。
|
||||
- 根因:旧脚本从参数/剪贴板读取,并自行 ⌘V;Handy 0.8.3 实际从 stdin 传入转写、读取 stdout 作为最终文本。
|
||||
- 修复:插件改为纯过滤器 `stdin → 回廊洗字 → stdout`,不再操作剪贴板或模拟按键。
|
||||
- 验收标准:管道输入“纯客宝、卡罗亚”时,stdout 必须返回“存客宝、卡若AI”。
|
||||
|
||||
## 2026-07-12|v1.4 Handy输入阶段ASR纠错
|
||||
|
||||
### 目标
|
||||
|
||||
Handy 语音转写后、文字进入当前输入框前,自动调用卡若AI统一ASR纠错库。
|
||||
|
||||
### 链路
|
||||
|
||||
```text
|
||||
Handy录音
|
||||
-> SenseVoice zh-Hans转写
|
||||
-> handy_macos_asr_paste.sh
|
||||
-> apply_karuo_voice_corrections.py
|
||||
-> 卡若闽南口音_ASR纠错库.json
|
||||
-> 剪贴板
|
||||
-> 当前输入框
|
||||
```
|
||||
|
||||
### 修改
|
||||
|
||||
- Handy `paste_method` 改为 `external_script`。
|
||||
- `external_script_path` 指向已验证的 `handy_macos_asr_paste.sh`。
|
||||
- 新增 `enable_karuo_asr_correction_macos.sh`,自动备份配置、接线并重启 Handy。
|
||||
- 钩子新增 `KARUO_ASR_TEST_ONLY=1` 无粘贴测试模式。
|
||||
- 补充 `卡罗AI/卡洛AI/卡路AI` 无空格纠错变体。
|
||||
|
||||
### 验证
|
||||
|
||||
- Handy 0.8.3 正常启动,SenseVoice 与 `zh-Hans` 保持不变。
|
||||
- Handy 日志确认加载 `ExternalScript` 和目标脚本。
|
||||
- 测试输入“打开纯客宝后台,卡罗AI帮我检查”输出“打开存客宝后台,卡若AI帮我检查”。
|
||||
- Cursor/Codex 对话层仍保留回廊洗字二次兜底。
|
||||
|
||||
### 回滚
|
||||
|
||||
恢复 `settings_store.json.bak.asr.*` 备份并重启 Handy,或把 `paste_method` 改回 `ctrl_v`、`external_script_path` 改回 `null`。
|
||||
170
01_卡资(金)/金仓_存储备份/服务器管理/scripts/东京服务器_TAT_状态检查与VPN安装.py
Normal file
170
01_卡资(金)/金仓_存储备份/服务器管理/scripts/东京服务器_TAT_状态检查与VPN安装.py
Normal file
@@ -0,0 +1,170 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""TAT 东京服务器: 状态检查 + 一键安装系统级 VPN(IKEv2 + L2TP)"""
|
||||
import base64, json, os, re, sys, time
|
||||
|
||||
|
||||
def _read_creds():
|
||||
"""从真源私密总表读腾讯云 Lighthouse 凭证(AKID8Q...)"""
|
||||
d = os.path.dirname(os.path.abspath(__file__))
|
||||
for _ in range(6):
|
||||
if os.path.basename(d) == "卡若AI":
|
||||
break
|
||||
d = os.path.dirname(d)
|
||||
p = os.path.join(d, "运营中枢", "私密配置", "账号密码与API私密总表.md")
|
||||
if not os.path.isfile(p):
|
||||
return None, None
|
||||
with open(p, "r", encoding="utf-8") as f:
|
||||
t = f.read()
|
||||
m = re.search(r"API SecretId\s*\|\s*`([^`]+)`", t)
|
||||
n = re.search(r"API SecretKey\s*\|\s*`([^`]+)`", t)
|
||||
if m and n:
|
||||
return m.group(1).strip(), n.group(1).strip()
|
||||
return None, None
|
||||
|
||||
|
||||
def find_tokyo_instance(cli):
|
||||
from tencentcloud.lighthouse.v20200324 import models
|
||||
req = models.DescribeInstancesRequest()
|
||||
r = cli.DescribeInstances(req)
|
||||
for inst in (r.InstanceSet or []):
|
||||
if "43.165.178.230" in (inst.PublicAddresses or []):
|
||||
return inst.InstanceId, inst.InstanceName, inst.InstanceState
|
||||
return None, None, None
|
||||
|
||||
|
||||
def tat_run(client, instance_id, content_b64, name, timeout=180):
|
||||
from tencentcloud.tat.v20201028 import models
|
||||
req = models.RunCommandRequest()
|
||||
req.Content = content_b64
|
||||
req.InstanceIds = [instance_id]
|
||||
req.CommandType = "SHELL"
|
||||
req.Timeout = timeout
|
||||
req.CommandName = name
|
||||
resp = client.RunCommand(req)
|
||||
return resp.InvocationId
|
||||
|
||||
|
||||
def tat_get_result(client, inv_id, wait_sec=40):
|
||||
from tencentcloud.tat.v20201028 import models
|
||||
print(f" 等待 {wait_sec}s 取回结果...")
|
||||
time.sleep(wait_sec)
|
||||
req = models.DescribeInvocationTasksRequest()
|
||||
f = models.Filter()
|
||||
f.Name = "invocation-id"
|
||||
f.Values = [inv_id]
|
||||
req.Filters = [f]
|
||||
r = client.DescribeInvocationTasks(req)
|
||||
for t in (r.InvocationTaskSet or []):
|
||||
tr = getattr(t, "TaskResult", None)
|
||||
if not tr:
|
||||
continue
|
||||
try:
|
||||
jj = json.loads(tr) if isinstance(tr, str) else tr
|
||||
out = jj.get("Output", "")
|
||||
if out:
|
||||
out = base64.b64decode(out).decode("utf-8", errors="replace")
|
||||
print("--- 服务器输出 ---\n" + out[:8000] + "\n---")
|
||||
except Exception as e:
|
||||
print(f" 解析结果失败: {e}\n 原始: {str(tr)[:600]}")
|
||||
|
||||
|
||||
def main():
|
||||
sid, skey = _read_creds()
|
||||
if not sid or not skey:
|
||||
print("❌ 未读到腾讯云凭证")
|
||||
return 1
|
||||
print(f" 凭证: {sid[:8]}...{skey[-6:]}")
|
||||
|
||||
from tencentcloud.common import credential
|
||||
from tencentcloud.lighthouse.v20200324 import lighthouse_client as ls_cli
|
||||
from tencentcloud.tat.v20201028 import tat_client as t_cli
|
||||
|
||||
REGION = "ap-tokyo"
|
||||
cred = credential.Credential(sid, skey)
|
||||
ls = ls_cli.LighthouseClient(cred, REGION)
|
||||
iid, iname, istate = find_tokyo_instance(ls)
|
||||
if not iid:
|
||||
print("❌ 在 ap-tokyo 区域未找到 43.165.178.230 的实例")
|
||||
return 1
|
||||
print(f" 服务器: {iname} ID={iid} 状态={istate}")
|
||||
|
||||
# ===== 1. 状态检查 =====
|
||||
cmd_status = r'''
|
||||
echo "=== 1. 服务器基础信息 ==="
|
||||
uname -a
|
||||
cat /etc/os-release 2>/dev/null | head -3
|
||||
echo ""
|
||||
|
||||
echo "=== 2. 资源 ==="
|
||||
uptime
|
||||
echo "磁盘:"; df -h / | tail -2
|
||||
echo "内存:"; free -h | head -2
|
||||
echo ""
|
||||
|
||||
echo "=== 3. 关键服务 ==="
|
||||
for s in strongswan xl2tpd nginx bt; do
|
||||
systemctl is-active $s 2>/dev/null || echo "$s: 未安装/未运行"
|
||||
done
|
||||
echo ""
|
||||
|
||||
echo "=== 4. 监听端口 ==="
|
||||
ss -tulnp 2>/dev/null | awk '/LISTEN|UNCONN/ {print $1, $5}' | sort -u | head -30
|
||||
echo ""
|
||||
|
||||
echo "=== 5. WireGuard / 进程 ==="
|
||||
ls /etc/wireguard/ 2>/dev/null || echo "wireguard: 未安装"
|
||||
wg show 2>/dev/null | head -10 || echo "wg: 未运行"
|
||||
echo ""
|
||||
|
||||
echo "=== 6. iptables 状态 ==="
|
||||
iptables -L INPUT -n 2>/dev/null | head -10
|
||||
echo ""
|
||||
|
||||
echo "=== 7. 腾讯云外网自检 ==="
|
||||
curl -s --max-time 5 https://api.ipify.org
|
||||
echo ""
|
||||
'''
|
||||
print("\n[1/2] TAT 状态检查...")
|
||||
tat = t_cli.TatClient(cred, REGION)
|
||||
inv = tat_run(tat, iid, base64.b64encode(cmd_status.encode()).decode(), "TokyoStatusCheck", timeout=60)
|
||||
print(f" 任务下发: {inv}")
|
||||
tat_get_result(tat, inv, wait_sec=20)
|
||||
|
||||
# ===== 2. 推送并执行 VPN 安装 =====
|
||||
script_path = os.path.join(os.path.dirname(__file__), "东京服务器_一键安装IKEv2_L2TP_VPN.sh")
|
||||
if not os.path.isfile(script_path):
|
||||
print(f"❌ 找不到脚本: {script_path}")
|
||||
return 1
|
||||
with open(script_path, "r", encoding="utf-8") as f:
|
||||
script_content = f.read()
|
||||
print(f"\n[2/2] 推送并执行 VPN 安装脚本 ({len(script_content)} bytes)...")
|
||||
install_cmd = (
|
||||
"cat > /tmp/vpn_install.sh << 'SCRIPT_EOF'\n"
|
||||
+ script_content
|
||||
+ "\nSCRIPT_EOF\n"
|
||||
"chmod +x /tmp/vpn_install.sh\n"
|
||||
"echo '脚本已写入 /tmp/vpn_install.sh,开始执行...'\n"
|
||||
"bash /tmp/vpn_install.sh 2>&1\n"
|
||||
)
|
||||
inv2 = tat_run(tat, iid, base64.b64encode(install_cmd.encode()).decode(), "TokyoInstallVPN", timeout=300)
|
||||
print(f" 安装任务下发: {inv2}")
|
||||
tat_get_result(tat, inv2, wait_sec=90)
|
||||
|
||||
print("\n" + "="*60)
|
||||
print(" ✅ TAT 流程结束")
|
||||
print("="*60)
|
||||
print("\n下一步用户在腾讯云控制台操作:")
|
||||
print(" 轻量应用服务器 → 43.165.178.230 → 防火墙 → 添加规则:")
|
||||
print(" 500/UDP 4500/UDP 1701/UDP (来源 0.0.0.0/0)")
|
||||
print("\n然后 macOS 系统设置 → 网络 → 添加 VPN 配置:")
|
||||
print(" • IKEv2(系统原生,零中间件): 服务器 43.165.178.230 · 远程ID 43.165.178.230")
|
||||
print(" 账号 karuo · 密码 KaruoVPN@2026")
|
||||
print(" • L2TP/IPSec(系统原生): 服务器 43.165.178.230 · 共享密钥 KaruoL2TP@2026")
|
||||
print(" 账号 karuo · 密码 KaruoVPN@2026")
|
||||
print(" • WireGuard(需装App,最快): 见 运营中枢/私密配置/房市通东京节点/wireguard-client.conf")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
196
01_卡资(金)/金仓_存储备份/服务器管理/scripts/东京服务器_一键安装IKEv2_L2TP_VPN.sh
Executable file
196
01_卡资(金)/金仓_存储备份/服务器管理/scripts/东京服务器_一键安装IKEv2_L2TP_VPN.sh
Executable file
@@ -0,0 +1,196 @@
|
||||
#!/bin/bash
|
||||
# ============================================================
|
||||
# 东京服务器一键安装系统级 VPN(macOS 系统设置可直接用)
|
||||
# - IKEv2/IPSec(strongSwan)— macOS 推荐
|
||||
# - L2TP/IPSec(xl2tpd + strongSwan)— 兼容老设备
|
||||
# 服务器:43.165.178.230(腾讯云东京 Lighthouse)
|
||||
# ============================================================
|
||||
set -e
|
||||
echo "=================================================="
|
||||
echo " 卡若AI · 东京服务器系统 VPN 一键安装"
|
||||
echo " 开始时间: $(date '+%F %T')"
|
||||
echo "=================================================="
|
||||
|
||||
# 1. 防火墙放行(必须)
|
||||
echo ""
|
||||
echo ">>> 1. 放行 VPN 端口(腾讯云防火墙在控制台另开)"
|
||||
iptables -I INPUT -p udp --dport 500 -j ACCEPT 2>/dev/null || true
|
||||
iptables -I INPUT -p udp --dport 4500 -j ACCEPT 2>/dev/null || true
|
||||
iptables -I INPUT -p udp --dport 1701 -j ACCEPT 2>/dev/null || true
|
||||
iptables -I INPUT -p udp --dport 50 -j ACCEPT 2>/dev/null || true
|
||||
iptables -I INPUT -p udp --dport 51 -j ACCEPT 2>/dev/null || true
|
||||
ufw allow 500/udp 2>/dev/null || true
|
||||
ufw allow 4500/udp 2>/dev/null || true
|
||||
ufw allow 1701/udp 2>/dev/null || true
|
||||
echo " 端口 500/4500/1701/50/51 已放行"
|
||||
|
||||
# 2. 开启 IP 转发
|
||||
echo ""
|
||||
echo ">>> 2. 开启 IP 转发"
|
||||
grep -q "net.ipv4.ip_forward=1" /etc/sysctl.conf || echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
|
||||
sysctl -w net.ipv4.ip_forward=1 >/dev/null
|
||||
echo " IP 转发已开启"
|
||||
|
||||
# 3. 安装 strongSwan + xl2tpd
|
||||
echo ""
|
||||
echo ">>> 3. 安装 strongSwan / xl2tpd / ppp"
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
if command -v apt-get >/dev/null 2>&1; then
|
||||
apt-get update -qq
|
||||
apt-get install -y -qq strongswan strongswan-pki libstrongswan-extra-plugins xl2tpd ppp
|
||||
elif command -v yum >/dev/null 2>&1; then
|
||||
yum install -y epel-release
|
||||
yum install -y strongswan xl2tpd ppp
|
||||
fi
|
||||
|
||||
# 4. 生成证书与密钥
|
||||
echo ""
|
||||
echo ">>> 4. 生成 IKEv2 证书与账号"
|
||||
mkdir -p /etc/ipsec.d/{certs,private,cacerts}
|
||||
cd /etc/ipsec.d
|
||||
# CA 私钥
|
||||
strongswan pki --gen --type rsa --size 2048 --outform pem > /etc/ipsec.d/private/caKey.pem 2>/dev/null
|
||||
# CA 证书
|
||||
strongswan pki --self --ca --lifetime 3650 \
|
||||
--in /etc/ipsec.d/private/caKey.pem \
|
||||
--type rsa --dn "C=CN, O=KaruoAI, CN=KaruoAI VPN CA" \
|
||||
--outform pem > /etc/ipsec.d/cacerts/caCert.pem 2>/dev/null
|
||||
# 服务器私钥
|
||||
strongswan pki --gen --type rsa --size 2048 --outform pem > /etc/ipsec.d/private/serverKey.pem 2>/dev/null
|
||||
# 服务器证书
|
||||
strongswan pki --pub --in /etc/ipsec.d/private/serverKey.pem --type rsa \
|
||||
| strongswan pki --issue --lifetime 3650 --cacert /etc/ipsec.d/cacerts/caCert.pem \
|
||||
--cakey /etc/ipsec.d/private/caKey.pem \
|
||||
--dn "C=CN, O=KaruoAI, CN=43.165.178.230" \
|
||||
--san "43.165.178.230" --flag serverAuth --flag ikeIntermediate \
|
||||
--outform pem > /etc/ipsec.d/certs/serverCert.pem 2>/dev/null
|
||||
echo " 证书已生成"
|
||||
|
||||
# 5. strongSwan 配置(IKEv2)
|
||||
echo ""
|
||||
echo ">>> 5. 配置 strongSwan IKEv2"
|
||||
cat > /etc/ipsec.conf <<'EOF'
|
||||
config setup
|
||||
charondebug="ike 1, knl 1, cfg 0"
|
||||
uniqueids=no
|
||||
|
||||
conn ikev2-vpn
|
||||
auto=add
|
||||
compress=no
|
||||
type=tunnel
|
||||
keyexchange=ikev2
|
||||
fragmentation=yes
|
||||
forceencaps=yes
|
||||
dpdaction=clear
|
||||
dpddelay=300s
|
||||
rekey=no
|
||||
left=%any
|
||||
leftid=43.165.178.230
|
||||
leftcert=serverCert.pem
|
||||
leftsendcert=always
|
||||
leftsubnet=0.0.0.0/0
|
||||
right=%any
|
||||
rightid=%any
|
||||
rightauth=eap-mschapv2
|
||||
rightsourceip=10.10.10.0/24
|
||||
rightdns=8.8.8.8,1.1.1.1
|
||||
rightsendcert=never
|
||||
eap_identity=%identity
|
||||
EOF
|
||||
|
||||
# 账号密码(PSK + 用户名/密码)
|
||||
cat > /etc/ipsec.secrets <<'EOF'
|
||||
: RSA serverKey.pem
|
||||
karuo : EAP "KaruoVPN@2026"
|
||||
EOF
|
||||
chmod 600 /etc/ipsec.secrets
|
||||
|
||||
# 6. xl2tpd 配置(L2TP/IPSec)
|
||||
echo ""
|
||||
echo ">>> 6. 配置 L2TP/IPSec"
|
||||
cat > /etc/xl2tpd/xl2tpd.conf <<'EOF'
|
||||
[global]
|
||||
ipsec saref = yes
|
||||
listen-addr = 43.165.178.230
|
||||
|
||||
[lns default]
|
||||
ip range = 10.10.11.10-10.10.11.250
|
||||
local ip = 10.10.11.1
|
||||
require chap = yes
|
||||
refuse pap = yes
|
||||
require authentication = yes
|
||||
name = l2tpd
|
||||
pppoptfile = /etc/ppp/options.xl2tpd
|
||||
length bit = yes
|
||||
EOF
|
||||
|
||||
cat > /etc/ppp/options.xl2tpd <<'EOF'
|
||||
ipcp-accept-local
|
||||
ipcp-accept-remote
|
||||
ms-dns 8.8.8.8
|
||||
ms-dns 1.1.1.1
|
||||
noccp
|
||||
auth
|
||||
crtscts
|
||||
idle 1800
|
||||
mtu 1410
|
||||
mru 1410
|
||||
lock
|
||||
proxyarp
|
||||
lcp-echo-interval 30
|
||||
lcp-echo-failure 4
|
||||
EOF
|
||||
|
||||
# L2TP 共享密钥 + 用户
|
||||
cat >> /etc/ipsec.secrets <<'EOF'
|
||||
43.165.178.230 : PSK "KaruoL2TP@2026"
|
||||
EOF
|
||||
echo 'karuo l2tpd "KaruoVPN@2026" *' > /etc/ppp/chap-secrets
|
||||
chmod 600 /etc/ppp/chap-secrets
|
||||
|
||||
# strongSwan 也允许 L2TP
|
||||
cat >> /etc/ipsec.conf <<'EOF'
|
||||
|
||||
conn l2tp-psk
|
||||
auto=add
|
||||
type=transport
|
||||
keyexchange=ikev1
|
||||
authby=secret
|
||||
left=%any
|
||||
leftprotoport=17/1701
|
||||
right=%any
|
||||
rightprotoport=17/%any
|
||||
rekey=no
|
||||
EOF
|
||||
chmod 600 /etc/ipsec.secrets
|
||||
|
||||
# 7. 启动服务
|
||||
echo ""
|
||||
echo ">>> 7. 启动 strongSwan / xl2tpd"
|
||||
systemctl enable strongswan xl2tpd 2>/dev/null || true
|
||||
systemctl restart strongswan 2>/dev/null || /etc/init.d/strongswan restart 2>/dev/null
|
||||
systemctl restart xl2tpd 2>/dev/null || /etc/init.d/xl2tpd restart 2>/dev/null
|
||||
|
||||
sleep 2
|
||||
echo ""
|
||||
echo ">>> 8. 状态检查"
|
||||
ss -ulnp | grep -E ":500|:4500|:1701" || true
|
||||
ipsec status 2>/dev/null | head -10 || true
|
||||
|
||||
echo ""
|
||||
echo "=================================================="
|
||||
echo " 安装完成!"
|
||||
echo " 服务器:43.165.178.230"
|
||||
echo ""
|
||||
echo " ★ IKEv2(macOS 推荐·原生)"
|
||||
echo " 服务器地址: 43.165.178.230"
|
||||
echo " 远程 ID: 43.165.178.230"
|
||||
echo " 账号: karuo"
|
||||
echo " 密码: KaruoVPN@2026"
|
||||
echo ""
|
||||
echo " ★ L2TP/IPSec(备用)"
|
||||
echo " 服务器地址: 43.165.178.230"
|
||||
echo " 共享密钥: KaruoL2TP@2026"
|
||||
echo " 账号: karuo"
|
||||
echo " 密码: KaruoVPN@2026"
|
||||
echo "=================================================="
|
||||
@@ -3,7 +3,7 @@
|
||||
# 原功能:豆包「全局语音输入」固定为 Option+1(macOS 显示 ⌥1 = ALT+1)
|
||||
|
||||
set -euo pipefail
|
||||
echo "❌ 豆包已卸载。语音输入请用:01_卡资(金)/金仓_存储备份/Handy中文语音/SKILL.md" >&2
|
||||
echo "❌ 豆包已卸载。语音输入请用:02_卡人(水)/水溪_整理归档/语音输入/SKILL.md" >&2
|
||||
exit 2
|
||||
DOUBAO="$HOME/Library/Application Support/Doubao"
|
||||
TARGET="Option+1"
|
||||
|
||||
BIN
02_卡人(水)/水岸_项目管理/卡若创业派对/每日文章飞书分发/exports/第238场_20260725/图1.png
Normal file
BIN
02_卡人(水)/水岸_项目管理/卡若创业派对/每日文章飞书分发/exports/第238场_20260725/图1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
BIN
02_卡人(水)/水岸_项目管理/卡若创业派对/每日文章飞书分发/exports/第238场_20260725/图2.png
Normal file
BIN
02_卡人(水)/水岸_项目管理/卡若创业派对/每日文章飞书分发/exports/第238场_20260725/图2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
BIN
02_卡人(水)/水岸_项目管理/卡若创业派对/每日文章飞书分发/exports/第238场_20260725/图3.png
Normal file
BIN
02_卡人(水)/水岸_项目管理/卡若创业派对/每日文章飞书分发/exports/第238场_20260725/图3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.7 MiB |
@@ -0,0 +1,127 @@
|
||||
关注派对房号 FM29037620
|
||||
|
||||
第238场|AI开始替你做事,最先要学会的不是提速,是设边界
|
||||
|
||||
AI真正开始接手执行之后,最值钱的能力不再是会不会让它干活,而是能不能把权限、数据和成交边界管住。
|
||||
|
||||
2026年7月24日,约1小时36分。
|
||||
|
||||
早场有人拿着刚买的软件上麦,说以前提取一个视频文件要手动敲很久,现在几秒钟就能完成。那种高兴很真实——不是因为省下了几分钟,而是忽然看见,原来自己卡了很久的事,也可以有更轻一点的做法。
|
||||
|
||||
派对里的小伙伴随即聊到收费工具、海外软件和AI能力差异。话题很快从“要不要花钱”变成了另一个更现实的问题:
|
||||
|
||||
工具升级以后,人到底能不能把省下来的时间换成结果?
|
||||
|
||||
再往后,讨论转到了AI权限、数据备份、广告交互、游戏IP和私域运营。 听起来跨度很大,但底层是一件事
|
||||
|
||||
——以前我们买工具、买流量、买账号;
|
||||
|
||||
现在,真正要为“谁可以执行、执行到哪一步、出了问题怎么恢复”负责。
|
||||
|
||||
工具贵不贵,不能只看价格
|
||||
|
||||
那位做文案和视频处理的小伙伴说,原来很多动作都靠手敲,后来装了一个提取工具,几秒钟就能完成过去几分钟的操作。
|
||||
|
||||
有人问:“那收费软件要不要试?”
|
||||
|
||||
答案很直接:该付费就付费。真正该算的不是软件每月多少钱,而是它帮你省下了多少时间,能不能让你多完成几次交付。
|
||||
|
||||
便宜工具如果让你反复返工,最后付出的不是月费,而是机会成本。
|
||||
|
||||
当然,付费也不等于随便买。软件来源、账号权限、数据安全和服务条款都要看清楚。尤其是需要外部网络或第三方账号的工具,应该使用合规渠道和官方支持的方式,不要把“能不能绕过去”当成能力。
|
||||
|
||||
派对里的小伙伴接了一句:“以前觉得AI只是聊天,现在好像真的能帮忙做事了。”
|
||||
|
||||
这句话说到了变化。前一段时间,大家更多是在让AI写文案、做表格、整理工作流;现在,AI越来越像一个能执行任务的助手。你给它目标、资料和权限,它就会继续往下推进。
|
||||
|
||||
但权限一旦放出去,风险也跟着出去。企业的电脑、知识库、账号、客户资料和自动化任务,不能因为“AI很聪明”就全部交给它。
|
||||
|
||||
真正成熟的用法是:让AI做清楚可回滚的事情,把关键操作设成确认点,重要文件按小时或按周期备份,出了问题能够恢复。这样才是效率,不是把所有按钮都交出去以后祈祷它别出错。
|
||||
|
||||
AI的下一阶段,是安全边界和真实成交
|
||||
|
||||
AI变化已经快到以周为单位。以前大家比的是谁的模型更强、谁的算力更多;
|
||||
|
||||
现在越来越多的企业开始关心,AI能不能在业务流程里稳定执行,能不能完成一笔真实成交,能不能不越权、不乱改、不把客户数据带错地方。
|
||||
|
||||
这也是安全边界的重要性。
|
||||
|
||||
AI可以帮你分析客户、整理资料、生成内容、跟进任务,但哪些信息可以读取,哪些动作需要人工确认,哪些事情绝对不能碰,必须提前写清楚。
|
||||
|
||||
不要把“自动化”理解成“完全不用管”。
|
||||
|
||||
自动化真正成熟的样子,是把人从重复动作里解放出来,把人留在判断、授权和责任的位置上。
|
||||
|
||||
这一点放到广告里也很明显。传统信息流广告是刷到、点开、购买; 以后更像是一个虚拟角色主动和你沟通,根据你的兴趣介绍产品。 ta可能更像销售,也更容易让人放下戒心。
|
||||
|
||||
这对平台和商家都是提醒:人设可以帮助沟通,但不能用来伪装身份、诱导消费;
|
||||
|
||||
AI可以提高触达效率,但不能替代真实的产品和售后。流量越像真人,规则越要写在前面。
|
||||
|
||||
数据也一样。企业把资料放进知识库,把任务交给AI之前,至少要知道谁能看、谁能改、多久备份一次、异常时怎么恢复。能跑起来只是第一关,能安全地跑一年,才叫产品。
|
||||
|
||||
游戏IP的价值,不在违规捷径
|
||||
|
||||
自己很早就接触游戏行业,后来把游戏主播、粉丝、周边产品和社群运营串起来,尝试把游戏影响力转成长期客户关系。
|
||||
|
||||
这件事的价值,不在于某一个游戏,而在于游戏IP本身可以承载什么:内容、粉丝、周边、陪练、赛事、品牌合作,甚至是围绕玩家需求建立的正规服务。
|
||||
|
||||
派对里的小伙伴问,为什么不直接做游戏俱乐部?
|
||||
|
||||
答案是,俱乐部和IP运营不是一回事。俱乐部负责培养和管理选手,IP运营更关心粉丝沉淀、内容持续和商业合作。两者可以合作,但边界、分工和收入来源要提前说清楚。
|
||||
|
||||
有人提到账号抵押、游戏资产和典当。这里的边界更不能模糊: 涉及金融、典当、账号交易或数字资产,必须有合法主体、合规牌照、明确估值和税务处理. 没有资质就不要把高风险业务包装成“副业机会”。
|
||||
|
||||
能赚钱的项目,不等于什么都能碰;长期做生意,先把牌照、规则和责任放在桌面上。
|
||||
|
||||
真正稳的游戏私域,应该是围绕合法内容和服务做沉淀:主播直播,团队负责运营,粉丝进入明确的服务流程,产品和合作方都能核验。
|
||||
|
||||
客户不是一串可以随意搬运的数字,粉丝也不是拿来反复骚扰的流量。
|
||||
|
||||
先看同行能不能做,再看自己能不能扛
|
||||
|
||||
有人问,判断一个项目值不值得做,是不是看同行已经赚到钱就行?
|
||||
|
||||
这只能算第一步。同行赚钱,说明市场可能存在;
|
||||
|
||||
但你还要问三个问题:
|
||||
|
||||
自己有没有相同的资源;
|
||||
|
||||
能不能承受前期成本;
|
||||
|
||||
愿不愿意长期做交付。
|
||||
|
||||
一个做直播带货的人讲到,直播间、设备、办公和人员,每个月都要支出。真正开始之前,他已经知道自己要承担什么,而不是只看到别人晒出来的收入。
|
||||
|
||||
过去拉新很热闹,现在更重要的是用户资产数字化: 一个用户一年能产生多少价值,服务成本是多少,复购和转化如何记录。 数据只有被长期跟踪,才有资格拿去谈合作或融资。
|
||||
|
||||
这和“先做小、再放大”是一个逻辑。不要一上来就投很大的钱,也不要为了证明自己敢干,先签下无法承受的成本。用较小的投入跑几轮,看客户是否愿意留下,再决定要不要加人、加设备、加渠道。
|
||||
|
||||
派对里的小伙伴说:“我就是不知道AI到底怎么挣钱。”
|
||||
|
||||
我接得很简单:AI本身只是工具,挣钱的地方在于它帮你降低了什么成本,提升了什么人效,或者让哪一种服务可以被稳定交付。
|
||||
|
||||
比如,一个有几万客户的私域团队,不可能每天手动看完每个人的朋友圈、聊天阶段和生日。 AI可以帮助整理客户行为、提醒回访、分配意向,但最后的沟通和成交仍然需要人判断。
|
||||
|
||||
这不是用AI替代所有人,而是让有限的人服务更多真正有价值的客户。客户越多,越不能只靠记忆;数据越多,越要把人留在关键判断上。
|
||||
|
||||
规则先于速度,稳定才是最便宜的增长
|
||||
|
||||
这一场有一个很清楚的变化:机会没有消失,只是门槛从“会不会操作”变成了“能不能稳定交付”。
|
||||
|
||||
软件要正规,数据要保护,AI要设权限,游戏项目要守边界,私域要有客户价值,合作要把责任讲清楚。速度当然重要,但没有规则的速度,往往只是更快撞上问题。
|
||||
|
||||
做项目的人也不需要一下子把未来十年想明白。两三个星期看一次变化,重新检查自己的工具、流程和客户;
|
||||
|
||||
能优化就优化,不能成立的就停下来。别把预测未来当成逃避今天交付的理由。
|
||||
|
||||
最后留下三个动作:
|
||||
|
||||
第一,把正在使用的AI工具列出来,标清楚它能读什么、能改什么、谁能授权。
|
||||
|
||||
第二,把自己的客户和粉丝分成“触达、服务、成交、复购”几个阶段,别只盯着人数。
|
||||
|
||||
第三,选一个小项目做一轮真实交付,用成本和结果判断能不能继续,不用想象替自己赚钱。
|
||||
|
||||
看得懂机会只是开始。能把机会做成产品,把产品做成服务,把服务做得安全、稳定、可复用,才是真正的能力。#派对会员 想继续跟上AI和商业变化,来派对里听真实案例,有问题直接 @我。
|
||||
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"last_sent_field": 237,
|
||||
"chapter_id": "10.96",
|
||||
"title": "第237场|洛杉矶做数据中心的人,回到教育AI先算清成本",
|
||||
"sent_at": "2026-07-23T12:01:35"
|
||||
"last_sent_field": 238,
|
||||
"chapter_id": "10.97",
|
||||
"title": "第238场|AI开始替你做事,最先要学会的不是提速,是设边界",
|
||||
"sent_at": "2026-07-25T06:23:25"
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ from urllib.parse import urlparse
|
||||
from datetime import datetime
|
||||
|
||||
import requests
|
||||
from typing import Optional
|
||||
|
||||
SCRIPT_DIR = Path(__file__).resolve().parent
|
||||
CONFIG = {
|
||||
@@ -77,7 +78,7 @@ def refresh_user_token(tokens):
|
||||
return None
|
||||
|
||||
|
||||
def get_token(node_token: str) -> str | None:
|
||||
def get_token(node_token: str) -> Optional[str]:
|
||||
"""优先 FEISHU_TOKEN,否则用 .feishu_tokens.json 的 user access_token(必要时刷新)。"""
|
||||
env_token = os.environ.get("FEISHU_TOKEN", "").strip()
|
||||
if env_token:
|
||||
@@ -111,14 +112,14 @@ def get_token(node_token: str) -> str | None:
|
||||
return None
|
||||
|
||||
|
||||
def wiki_url_to_node_token(url: str) -> str | None:
|
||||
def wiki_url_to_node_token(url: str) -> Optional[str]:
|
||||
"""从 Wiki 页面 URL 解析 node_token。"""
|
||||
# .../wiki/NAMUwdbuFiAy2GkmMZ3clXynnwc 或 .../wiki/xxx?...
|
||||
m = re.search(r"/wiki/([A-Za-z0-9_-]+)", url)
|
||||
return m.group(1) if m else None
|
||||
|
||||
|
||||
def get_wiki_node(token: str, node_token: str) -> dict | None:
|
||||
def get_wiki_node(token: str, node_token: str) -> Optional[dict]:
|
||||
"""获取 wiki 节点信息,返回 node 字典(含 obj_token, obj_type)。"""
|
||||
r = requests.get(
|
||||
f"https://open.feishu.cn/open-apis/wiki/v2/spaces/get_node?token={node_token}",
|
||||
@@ -165,7 +166,7 @@ def get_wiki_child_nodes(token: str, space_id: str, parent_node_token: str) -> l
|
||||
return out
|
||||
|
||||
|
||||
def collect_file_tokens_from_sheet(token: str, spreadsheet_token: str) -> list[str]:
|
||||
def collect_file_tokens_from_sheet(token: str, spreadsheet_token: str) -> list:
|
||||
"""从电子表格中收集浮动图片的 file_token / float_image_token。"""
|
||||
headers = {"Authorization": f"Bearer {token}"}
|
||||
file_tokens = []
|
||||
@@ -247,7 +248,7 @@ def _extract_file_tokens(obj, out: list):
|
||||
_extract_file_tokens(x, out)
|
||||
|
||||
|
||||
def collect_file_tokens_from_docx(token: str, document_id: str) -> list[str]:
|
||||
def collect_file_tokens_from_docx(token: str, document_id: str) -> list:
|
||||
"""从 docx 文档拉取全部 blocks,收集图片/文件的 file_token。"""
|
||||
headers = {"Authorization": f"Bearer {token}"}
|
||||
file_tokens = []
|
||||
@@ -332,7 +333,7 @@ def download_file(url: str, save_path: Path, token: str) -> bool:
|
||||
return ret == 0
|
||||
|
||||
|
||||
def _collect_from_node(token: str, obj_token: str, obj_type: str) -> list[str]:
|
||||
def _collect_from_node(token: str, obj_token: str, obj_type: str) -> list:
|
||||
"""从单个节点(docx/sheet)收集 file_tokens。"""
|
||||
if obj_type == "sheet":
|
||||
return collect_file_tokens_from_sheet(token, obj_token)
|
||||
|
||||
@@ -5,10 +5,10 @@ description: 卡若记忆宫殿命名体系 · 间名「回廊洗字」:口述
|
||||
group: 水
|
||||
memory_palace_path: 卡若记忆宫殿/水殿/水溪厢/回廊洗字
|
||||
memory_palace_slot: 口述与 ASR 杂声在此洗净,再入记忆与字幕流水线
|
||||
triggers: **回廊洗字、卡若记忆宫殿命名体系、记忆宫殿、记忆空间、语音转写纠错、语音输入、闽南话、闽南口音、听写、ASR、转写纠错、纠错库、误听、卡罗拉、卡罗伊**、口述、嘴瓢、**网页CLI、终端浏览器、命令行看网页、browsh**
|
||||
triggers: **回廊洗字、卡若记忆宫殿命名体系、记忆宫殿、记忆空间、语音转写纠错、闽南话、闽南口音、听写、ASR、转写纠错、纠错库、误听、卡罗拉、卡罗伊**、口述、嘴瓢、**网页CLI、终端浏览器、命令行看网页、browsh**
|
||||
owner: 水溪
|
||||
version: "1.7.0"
|
||||
updated: "2026-07-18"
|
||||
version: "1.8.0"
|
||||
updated: "2026-07-24"
|
||||
---
|
||||
|
||||
# 回廊洗字(W03b · 卡若记忆宫殿命名体系)
|
||||
@@ -34,7 +34,7 @@ updated: "2026-07-18"
|
||||
|
||||
1. **在推理与执行前**,将用户本轮自然语言视为可能含 ASR 噪声;在心中或用下述脚本对**整段或关键片段**做一次纠正后再定意图(不必改用户原文展示,**内部理解**以纠正后为准)。
|
||||
2. **统一过滤**:不仅「卡路 / 卡罗 / 卡洛」类,**Skill / Codex / GitHub / Firecrawl / Handy / Webhook / MongoDB / ASR / Cursor / Claude / Soul / 私域 / 存客宝 / 流量池** 等表中近音一并处理;以 `卡若闽南口音_ASR纠错库.json` 为**唯一**词条源。
|
||||
3. **产品用词(存客宝/触客宝)**:口述或 ASR 写出 **「纯客宝」「纯课宝」** 时,一律理解为 **「存客宝」**;写出 **「流量词」** 时,一律理解为 **「流量池」**(旧称已废弃;技术字段 `poolKeywords` 不变)。
|
||||
3. **产品用词(存客宝/触客宝)**:口述或 ASR 写出 **「纯客宝」「纯课宝」** 时,一律理解为 **「存客宝」**;写出 **「流量词」** 时,一律理解为 **「流量池」**(旧称已废弃;技术字段 `poolKeywords` 不变)。 **该归一必须在每轮会话的理解、路由、工具参数整理与新增文档写入前完成;历史原文保持原样。**
|
||||
4. 替换顺序:**按 key 长度降序**全文替换,避免短词截断长词(与 `soul_enhance.py`、`apply_karuo_voice_corrections.py` 一致)。
|
||||
5. **上下文**:若替换后与句义冲突(食物热量、受伤、西游记原著等),以语义为准,见 JSON `notes` 与下文「终端网页 CLI」段。
|
||||
6. 专有名词:Cursor、Claude、Soul、卡若AI、**流量池**(非「流量词」)等按表中写法对齐。
|
||||
@@ -54,7 +54,7 @@ echo "卡罗拉更新 skill" | python3 ".../apply_karuo_voice_corrections.py"
|
||||
|
||||
| 项 | 路径 / 值 |
|
||||
|:---|:---|
|
||||
| **Skill 真源** | `01_卡资(金)/金仓_存储备份/Handy中文语音/SKILL.md`(**G25b**) |
|
||||
| **Skill 真源** | `02_卡人(水)/水溪_整理归档/语音输入/SKILL.md`(**W03h**) |
|
||||
| 一键总装 | `…/Handy中文语音/脚本/install_all.sh` |
|
||||
| macOS 写配置 | `…/脚本/handy_apply_chinese_macos.sh` |
|
||||
| 粘贴 | `paste_method=ctrl_v`(剪贴板 Cmd+V) |
|
||||
|
||||
13
02_卡人(水)/水溪_整理归档/语音转写纠错/参考资料/近期对话ASR纠错清单_20260724.md
Normal file
13
02_卡人(水)/水溪_整理归档/语音转写纠错/参考资料/近期对话ASR纠错清单_20260724.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# 近期对话 ASR 纠错清单(2026-07-24)
|
||||
|
||||
## 本轮加固
|
||||
|
||||
| 误听/转写 | 正写 | 固化位置 | 验收 |
|
||||
|---|---|---|---|
|
||||
| 纯客宝、纯课宝 | 存客宝 | 唯一 JSON 词库、W03b、Codex 入口 | 命令行回归通过 |
|
||||
|
||||
## 执行约束
|
||||
|
||||
- 每轮用户输入在意图理解、Skill 路由、工具参数整理与持久文档写入前执行一次内部 ASR 滤真。
|
||||
- 历史聊天原文不重写;新增文档与执行产物统一使用“存客宝”。
|
||||
- 词条按长度降序替换;词库仍是唯一真源。
|
||||
@@ -1,13 +1,13 @@
|
||||
---
|
||||
name: Handy中文语音
|
||||
description: macOS/Windows Handy 中文语音+语音唤醒卡若AI Agent(⌘1/Ctrl+1)。触发词:Handy、卡若AI语音、语音唤醒、呼叫卡若AI、全局语音、⌘1、Ctrl+1、SenseVoice、按住说话。
|
||||
group: 金
|
||||
memory_palace_path: 卡若记忆宫殿/金殿/金仓厢/听写台
|
||||
memory_palace_slot: 本机按住说话,中文落字;纠错在 Cursor 回廊洗字
|
||||
triggers: Handy、全局语音、卡若AI语音、语音唤醒、呼叫卡若AI、喊卡若AI、Windows Handy、⌘1、Ctrl+1、语音输入、SenseVoice、按住说话、Handy中文、handy_apply、语音Agent
|
||||
owner: 金仓
|
||||
version: "1.4"
|
||||
updated: "2026-07-12"
|
||||
name: 语音输入
|
||||
description: 卡水·水溪语音输入:Handy 中文转写、日常输入、配置基线、验收与排障。触发词:语音输入、Handy、卡若AI语音、Fn语音、SenseVoice。
|
||||
group: 水
|
||||
memory_palace_path: 卡若记忆宫殿/水殿/水溪厢/语音输入台
|
||||
memory_palace_slot: 本机语音转写、输入与配置基线;纠错交由回廊洗字
|
||||
triggers: 语音输入、Handy、卡若AI语音、Fn语音、Command+1、结合屏幕、免按住说话、SenseVoice、中文听写、语音唤醒、语音配置、Handy中文、语音Agent
|
||||
owner: 水溪
|
||||
version: "1.5"
|
||||
updated: "2026-07-26"
|
||||
related:
|
||||
- 01_卡资(金)/金仓_存储备份/Cursor持久对话/SKILL.md
|
||||
- 02_卡人(水)/水溪_整理归档/语音转写纠错/SKILL.md
|
||||
@@ -15,9 +15,9 @@ related:
|
||||
- 运营中枢/参考资料/卡若闽南口音_ASR纠错库.json
|
||||
---
|
||||
|
||||
# Handy中文语音(G25b · 金仓)
|
||||
# 语音输入(W03h · 水溪)
|
||||
|
||||
> **一句话**:**macOS**(Apple Silicon · ⌘1 · CoreML)与 **Windows**(x64/ARM64 · Ctrl+1 · DirectML)用 **Handy 0.8.3** + **SenseVoice** 按住说话→**先回廊洗字再自动输入**(如 纯客宝→存客宝);五笔输入法不变。
|
||||
> **一句话**:**macOS** 当前使用 **Fn 开始录音、再按 Fn 结束录音**,无需按住;Handy 0.8.3 + SenseVoice 输出中文并自动填入当前输入框。ASR 纠错由 W03b「回廊洗字」在对话理解层统一处理。
|
||||
|
||||
## 适用场景
|
||||
|
||||
@@ -30,10 +30,17 @@ related:
|
||||
| **语音唤 Agent** | 说「**卡若AI** + 任务」或「**火炬/金仓** + 任务」→ pipeline 静默 Hub / 官网(见下) |
|
||||
| **语音 · 需求整理** | 先说 **小乖/乖乖/小宝**,再说「整理需求 …」→ 直投 Hub **语音·需求整理**(**不粘贴**输入框;见 `setup_handy_voice_exec_wake.sh`) |
|
||||
|
||||
|
||||
## 结合屏幕(Command+1)
|
||||
|
||||
- **Fn**:普通输入,保持现有“按一次开始、再按一次结束”。
|
||||
- **Command+1**:截取一张当前主屏后开始/结束语音;转写完成会填入截图路径与洗字后的上下文指令。
|
||||
- 使用、权限、验收、回滚见:[结合屏幕本地部署与使用.md](references/结合屏幕本地部署与使用.md)。
|
||||
|
||||
## 一键安装(Agent / 终端)
|
||||
|
||||
```bash
|
||||
bash "/Users/karuo/Documents/个人/卡若AI/01_卡资(金)/金仓_存储备份/Handy中文语音/脚本/install_all.sh"
|
||||
bash "/Users/karuo/Documents/个人/卡若AI/02_卡人(水)/水溪_整理归档/语音输入/脚本/install_all.sh"
|
||||
```
|
||||
|
||||
**可分发安装包**(`4、火_工具开发/工具/Handy中文语音/`):
|
||||
@@ -45,7 +52,7 @@ bash "/Users/karuo/Documents/个人/卡若AI/01_卡资(金)/金仓_存储备
|
||||
|
||||
打包(Mac 上执行,产出双平台 zip):`bash …/Handy中文语音/脚本/build_package.sh`
|
||||
|
||||
## 语音唤醒 · 卡若AI Agent 插件(G25b+)
|
||||
## 语音唤醒 · 卡若AI Agent 插件(W03h+)
|
||||
|
||||
口述 **「卡若AI …」** 或 **成员名 + 任务**(如「火炬 查 Agent 接口」)→ **不粘贴**,改为:
|
||||
|
||||
@@ -116,8 +123,8 @@ pbpaste | python3 "/Users/karuo/Documents/个人/卡若AI/运营中枢/工作台
|
||||
|:---|:---|:---|
|
||||
| **模型** | 已下载 → **SenseVoice** 卡片 | **活跃** |
|
||||
| **通用** | **SenseVoice 设置 → 语言** | **简体中文**(勿「自动」) |
|
||||
| **通用** | **转录快捷键** | **⌘1**(点框后按 ⌘+1) |
|
||||
| **通用** | **按住说话** | **开** |
|
||||
| **通用** | **转录快捷键** | **Fn**(按一次开始、再按一次结束) |
|
||||
| **通用** | **按住说话** | **关**(Fn 切换录音) |
|
||||
| **高级 → 应用** | **开机启动** | **关**(防双实例冲配置) |
|
||||
| **高级 → 输出** | **粘贴方式** | **剪贴板 (Cmd+V)**(勿外部脚本) |
|
||||
| **高级 → 应用** | **实验性功能** | **开** → **实验性 → ONNX 加速** | **CoreML** |
|
||||
@@ -132,9 +139,9 @@ pbpaste | python3 "/Users/karuo/Documents/个人/卡若AI/运营中枢/工作台
|
||||
| `selected_language` | `zh-Hans` |
|
||||
| `paste_method` | `ctrl_v` |
|
||||
| `external_script_path` | `null` |
|
||||
| `bindings.transcribe.current_binding` | `command_left+1` |
|
||||
| `push_to_talk` | `true` |
|
||||
| `autostart_enabled` | `false` |
|
||||
| `bindings.transcribe.current_binding` | `fn` |
|
||||
| `push_to_talk` | `false` |
|
||||
| `autostart_enabled` | `true` |
|
||||
| `ort_accelerator` | `coreml` |
|
||||
|
||||
模型目录:`~/Library/Application Support/com.pais.handy/models/sense-voice-int8/`
|
||||
@@ -152,14 +159,14 @@ pbpaste | python3 "/Users/karuo/Documents/个人/卡若AI/运营中枢/工作台
|
||||
## 验收
|
||||
|
||||
1. `tail -30 ~/Library/Logs/com.pais.handy/handy.log` 含 `zh-Hans`、`sense-voice-int8`
|
||||
2. 记事本按住 **⌘1** 说「卡若语音输入测试」→ 松开 → 粘贴为**中文**
|
||||
2. 记事本按 **Fn** 开始说「卡若语音输入测试」→ 再按 **Fn** 结束 → 自动填入中文
|
||||
3. 五笔输入法未被 Handy 修改
|
||||
|
||||
## 排障速查
|
||||
|
||||
| 现象 | 处理 |
|
||||
|:---|:---|
|
||||
| 英文乱码 / yu pizza yin | 语言被改回 **auto** → UI 改简体中文 + 重跑 `handy_apply_chinese_macos.sh` |
|
||||
| 英文乱码 / yu pizza yin | 语言被改回 **auto** → UI 改简体中文;修改配置前先备份并确认 |
|
||||
| 转录为空 | 多为 **x86 Handy** → 重装 `handy_install_arm64.sh` |
|
||||
| 配置写了又变 | `killall -9 handy`(进程名小写 **handy**);关开机启动;勿开外部脚本 |
|
||||
| 两个 Handy | 活动监视器只留一个;`autostart_enabled=false` |
|
||||
@@ -171,7 +178,7 @@ pbpaste | python3 "/Users/karuo/Documents/个人/卡若AI/运营中枢/工作台
|
||||
- **禁止** `brew install --cask handy`(易 x86)
|
||||
- macOS 仅允许使用已验证的 `handy_macos_asr_paste.sh`;Handy 0.8.3 ExternalScript 只传 `$1` 且忽略 stdout,插件必须自行输入
|
||||
|
||||
## 2026-07-12 输入阶段纠错
|
||||
## 历史方案:2026-07-12 输入阶段纠错
|
||||
|
||||
Handy 已可在文字进入当前输入框前调用卡若AI统一词库:
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
# Handy 语音输入 KI|本机实测基线(2026-07-26)
|
||||
|
||||
> 归属:金仓 · G25b「Handy中文语音」
|
||||
> 目标:让卡若AI在电脑上稳定获得“说话 → 中文转写 → 回廊洗字 → 填入当前对话框”的能力。
|
||||
> 词库唯一真源:[卡若闽南口音_ASR纠错库.json](../../../../运营中枢/参考资料/卡若闽南口音_ASR纠错库.json)
|
||||
|
||||
## 1. 当前可用结论
|
||||
|
||||
| 项目 | 本机实测结果 |
|
||||
|---|---|
|
||||
| 应用 | `/Applications/Handy.app`,v0.8.3,Apple Silicon 版本 |
|
||||
| 输入设备 | MacBook Pro 麦克风,44.1kHz、单声道 |
|
||||
| 识别模型 | `sense-voice-int8`,中文 `zh-Hans` |
|
||||
| 操作 | 按 `Fn` 开始录音,再按 `Fn` 结束;转写后自动填进当前焦点输入框 |
|
||||
| 发送策略 | `auto_submit=false`:只填入,不自动发送;无需持续按住热键 |
|
||||
| 识别速度 | 最近实测 90–471ms(不含录音时长) |
|
||||
| 文本纠正 | 词库回归 3/3 通过;当前输入保留原有 Handy 行为,对话层继续进行语义级回廊洗字 |
|
||||
| 语音Hub | `http://127.0.0.1:13458/api/health` 返回 `ok=true` |
|
||||
|
||||
## 2. 当前固定链路
|
||||
|
||||
```text
|
||||
按 Fn 开始录音 → 再按 Fn 结束录音
|
||||
→ Handy 0.8.3 / SenseVoice(zh-Hans)
|
||||
→ 剪贴板(copy_to_clipboard)
|
||||
→ Handy Cmd+V 填入当前输入框
|
||||
→ Codex/卡若AI 对话层进行语义级回廊洗字兜底
|
||||
```
|
||||
|
||||
## 3. 本机配置基线
|
||||
|
||||
配置文件:`~/Library/Application Support/com.pais.handy/settings_store.json` 的 `settings`。
|
||||
|
||||
| 键 | 目标值 | 2026-07-26 回读 |
|
||||
|---|---|---|
|
||||
| `selected_language` | `zh-Hans` | 通过 |
|
||||
| `selected_model` | `sense-voice-int8` | 通过 |
|
||||
| `bindings.transcribe.current_binding` | `fn` | 通过 |
|
||||
| `push_to_talk` | `false`(无需按住) | 通过 |
|
||||
| `paste_method` | `ctrl_v` | 通过 |
|
||||
| `clipboard_handling` | `copy_to_clipboard` | 通过 |
|
||||
| `auto_submit` | `false` | 通过 |
|
||||
| `autostart_enabled` | `true` | 通过 |
|
||||
| `ort_accelerator` | `auto` | 通过 |
|
||||
|
||||
> 当前按用户确认的原配置运行:`external_script_path` 保持原路径,剪贴板守护不启用。
|
||||
|
||||
## 4. 已发现问题 → 根因 → 固化处理
|
||||
|
||||
| 现象 | 根因 | 固化处理 |
|
||||
|---|---|---|
|
||||
| 口述“卡路AI / 卡罗AI / hand of”等误听 | ASR 固有近音 | 保留原始 `Fn` 输入方式;由卡若AI对话层回廊洗字理解与词库迭代处理 |
|
||||
| 中文变英文或乱码 | `selected_language=auto` | 固定 `zh-Hans`,必要时在 Handy 设置页确认“简体中文” |
|
||||
| 转写为空 | 误装 x86 版 Handy | 仅使用 Apple Silicon / aarch64 安装包 |
|
||||
| 配置反复变化 | 双实例或手工脚本覆盖 | 保持用户确认的 `autostart_enabled=true`;修改前先备份并得到确认 |
|
||||
| 纠错后还要手动粘贴 | 输入焦点变化 | 保持 Handy 原有 CtrlV 自动填入;不额外安装洗字守护 |
|
||||
| CoreML 未实际启用 | v0.8.3 启动后把该项规范化为 `auto` | 当前不升级;若速度异常,再在隔离副本验证 v0.9.4 |
|
||||
|
||||
完整历史见 [gotchas.md](../evolution/gotchas.md)。
|
||||
|
||||
## 5. 日常使用
|
||||
|
||||
1. 把光标放到 Codex、网页、备忘录等输入框。
|
||||
2. **按 `Fn` 开始说话,再按 `Fn` 结束。**
|
||||
3. 检查洗后的文本后,再手动发送。
|
||||
4. 发现新误听:只向唯一 JSON 词库追加“误听→正写”,再跑回归测试。
|
||||
|
||||
## 6. 快速验收与诊断
|
||||
|
||||
```bash
|
||||
# 仅查看当前配置(不修改)
|
||||
python3 -c 'import json,pathlib; print(json.loads((pathlib.Path.home()/"Library/Application Support/com.pais.handy/settings_store.json").read_text())["settings"]["bindings"]["transcribe"]["current_binding"])'
|
||||
|
||||
# 词库回归
|
||||
python3 "/Users/karuo/Documents/个人/卡若AI/运营中枢/工作台/脚本/test_karuo_asr_corrections.py"
|
||||
|
||||
# 日志
|
||||
tail -60 "$HOME/Library/Logs/com.pais.handy/handy.log"
|
||||
tail -60 "$HOME/Library/Logs/karuo/handy_clipboard_wash.log"
|
||||
```
|
||||
|
||||
手工验收句:**“打开纯客宝后台,卡罗AI检查流量词。”**
|
||||
预期:文本自动填入;卡若AI在对话理解层将“纯客宝、卡罗AI、流量词”识别为“存客宝、卡若AI、流量池”。
|
||||
|
||||
## 7. 当前配置保护
|
||||
|
||||
- 当前用户确认基线:`Fn + 无需按住 + 中文 SenseVoice + CtrlV 自动填入`。
|
||||
- 配置修改前先创建 `settings_store.json.bak.*` 备份,并先向用户说明具体变更。
|
||||
- 本轮新增的 `com.karuo.handy-asr-wash` 已卸载。
|
||||
@@ -0,0 +1,102 @@
|
||||
# HeyClicky 调研与卡若AI语音输入迭代方案(2026-07-26)
|
||||
|
||||
> 归属:卡水·水溪 · W03h「语音输入」
|
||||
> 结论:保留当前 Handy 输入法,不替换;在其上叠加“上下文问答”和“任务投递”两条可选能力。
|
||||
|
||||
## 1. 调研真源
|
||||
|
||||
- 开源基础:[farzaa/clicky(MIT)](https://github.com/farzaa/clicky)
|
||||
- 产品现状:[heyclicky 官网](https://www.heyclicky.com/)
|
||||
- 数据边界:[heyclicky 隐私说明](https://www.heyclicky.com/privacy-policy)
|
||||
|
||||
开源 Clicky 的基础结构是:菜单栏应用、控制面板、透明屏幕叠层;热键触发后进行实时转写,再把“转写文本 + 当前截图”发给模型,结果以文字、TTS 和屏幕指针反馈。官方仓库说明其开源代码已冻结,后续产品功能在 HeyClicky 私有版本持续迭代。
|
||||
|
||||
## 2. HeyClicky 的运行模式
|
||||
|
||||
```text
|
||||
热键触发
|
||||
├─ 采集当前语音
|
||||
├─ 同一时刻抓取当前屏幕(仅触发时)
|
||||
├─ 意图分流:Talk / Agent
|
||||
├─ Talk:模型流式回复 → TTS + 指针标注
|
||||
└─ Agent:后台执行任务 → 回执与进度
|
||||
```
|
||||
|
||||
其产品核心不是“更快转写”,而是三个体验层:
|
||||
|
||||
1. **听写**:将语音写入当前文本框。
|
||||
2. **屏幕上下文问答**:用户正在看什么,助手就围绕什么解释、指路或生成内容。
|
||||
3. **任务代理**:用户明确说出 Agent 意图后,才投递后台任务。
|
||||
|
||||
## 3. 卡若AI当前链路
|
||||
|
||||
```text
|
||||
Fn 开始录音 → Fn 结束录音
|
||||
→ Handy / SenseVoice 中文转写
|
||||
→ CtrlV 自动填入当前输入框
|
||||
→ 卡若AI 对话层 W03b 回廊洗字
|
||||
→ Skill 路由 / Hub / Codex 执行
|
||||
```
|
||||
|
||||
当前优势:本机中文转写快、使用方式固定、无需按住、输入行为稳定。
|
||||
当前缺口:语音一律当作文本输入,缺少“我要问当前屏幕”与“我要派任务”的明确分流;没有统一的回执卡、上下文采集开关和效果指标。
|
||||
|
||||
## 4. 建议的目标运行模式
|
||||
|
||||
```text
|
||||
Fn 普通输入(保留原样)
|
||||
└─ 语音 → Handy → 当前输入框 → W03b 洗字
|
||||
|
||||
语音前缀「卡若AI,结合屏幕:…」(新增,可选)
|
||||
└─ 转写 → 单次屏幕快照 → 本机脱敏 → W03b 洗字
|
||||
→ 上下文问答 → 文本/可选朗读答复
|
||||
|
||||
语音前缀「卡若AI,执行:…」(新增,可选)
|
||||
└─ 转写 → W03b 洗字 → Skill 路由
|
||||
→ Hub 创建任务 → 进度回执 → 完成结果
|
||||
```
|
||||
|
||||
### 不变项
|
||||
|
||||
- **Fn 切换录音、无需按住**不变。
|
||||
- 继续使用 Handy + SenseVoice;不强制安装 Clicky,也不替换输入法。
|
||||
- 不开启常驻截屏;只在“结合屏幕”明确前缀下采一张当前屏幕。
|
||||
- 现有 W03b 仍是唯一 ASR 纠错源,W03h 仍是唯一语音输入入口。
|
||||
|
||||
## 5. 分期迭代
|
||||
|
||||
| 优先级 | 内容 | 验收标准 | 配置影响 |
|
||||
|---|---|---|---|
|
||||
| P0 | 语音三态识别:普通输入 / 结合屏幕 / 执行任务 | 20 条固定口令分流正确率≥95%;普通 Fn 行为不变 | 无,先做离线识别与日志 |
|
||||
| P1 | 单次屏幕上下文桥接 | 仅明确口令时截图;截图不落盘;问答回执含“已使用当前屏幕” | 新增明确开关,默认关闭 |
|
||||
| P1 | 统一语音回执卡 | 输入、纠错、路由、任务状态都能查看 | 无 |
|
||||
| P2 | 可选 TTS 与屏幕标注 | 可随时停止;只在结合屏幕模式启用 | 新增默认关闭开关 |
|
||||
| P2 | 语音质量看板 | 统计转写耗时、纠错命中率、分流正确率、失败原因 | 本机只存脱敏指标 |
|
||||
|
||||
## 6. 第一轮建议:只做 P0
|
||||
|
||||
先不动 Handy 配置,先加一个**本地语音意图分流器**:
|
||||
|
||||
- 没有前缀:继续原样填入输入框。
|
||||
- `卡若AI,结合屏幕`:标记为 `screen_question`,等待用户确认屏幕权限开关。
|
||||
- `卡若AI,执行` 或 `水溪/火炬/金仓` + 任务:标记为 `agent_task`,交给既有 Hub 和 Skill 路由。
|
||||
- 每次生成结构化回执:`原转写 / 洗字结果 / 意图 / 路由 / 耗时 / 结果`。
|
||||
|
||||
这一步复用已有 `karuo_voice_pipeline.py`、W03b 词库和 Hub `13458`,不引入新的桌面应用。
|
||||
|
||||
## 7. 与 HeyClicky 的取舍
|
||||
|
||||
| HeyClicky 做法 | 卡若AI采用方式 |
|
||||
|---|---|
|
||||
| 热键同时带屏幕截图 | 仅“结合屏幕”前缀下单次截图,默认关闭 |
|
||||
| Talk 与 Agent 分开计量 | 普通输入、结合屏幕、执行任务分三条逻辑链 |
|
||||
| TTS + 指针叠层 | 放到 P2,先验证真实使用频次 |
|
||||
| 云端代理处理音频与截图 | 先本机转写、词库与路由;截图仅按需传递 |
|
||||
| 通用 Mac 助手 | 保持卡若AI五行 Skill 与业务上下文优势 |
|
||||
|
||||
## 8. 验收样例
|
||||
|
||||
1. `Fn` 输入“打开纯客宝后台” → 正常填入;对话层理解为“打开存客宝后台”。
|
||||
2. `卡若AI,结合屏幕:这个报错是什么` → 仅一次截图、返回问题解释,不创建任务。
|
||||
3. `卡若AI,执行:整理今天的语音问题` → 路由 W03h/W03b 后创建任务,返回任务编号和进度。
|
||||
4. 不带前缀的任意语音 → 不截图、不启动 Agent、不改变当前输入行为。
|
||||
54
02_卡人(水)/水溪_整理归档/语音输入/references/结合屏幕本地部署与使用.md
Normal file
54
02_卡人(水)/水溪_整理归档/语音输入/references/结合屏幕本地部署与使用.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# 结合屏幕|本地部署与使用(W03h)
|
||||
|
||||
## 已部署运行模式
|
||||
|
||||
- 应用图标:`资源/卡若AI·屏语.png`,部署时自动生成并装入 macOS 原生 `.icns`。
|
||||
|
||||
```text
|
||||
Fn:普通语音输入(原行为不变)
|
||||
Command+1:结合屏幕模式
|
||||
第一次 Command+1(或菜单“开始结合屏幕”)→ 显示“正在截图 / 正在听你说话”悬浮状态
|
||||
第二次 Command+1(或菜单“结束并提交”)→ 显示“正在转写并提交”
|
||||
转写钩子 → 回廊洗字 → 生成「截图路径 + 指令」上下文包 → 自动提交给当前 AI → 直接获得回答
|
||||
```
|
||||
|
||||
## 语音区分
|
||||
|
||||
| 方式 | 含义 | 行为 |
|
||||
|---|---|---|
|
||||
| `Fn` 后直接说内容 | 普通输入 | 保持原有 Handy 自动填入 |
|
||||
| `Command+1` 后说“结合屏幕,……” | 结合屏幕 | 将单次截图与洗字指令一起发送到当前 AI 输入框 |
|
||||
| `Command+1` 后说“结合屏幕,执行……” | 结合屏幕任务 | 上下文包会要求当前 AI 先看截图,再执行指令 |
|
||||
|
||||
> Command+1 已经表示结合屏幕;说“结合屏幕”是为了让转写结果和后续 AI 语义更清晰。
|
||||
|
||||
## 首次启用
|
||||
|
||||
在 **系统设置 → 隐私与安全性 → 录屏与系统录音** 中开启 `卡若AI·屏语`。
|
||||
|
||||
`Command+1` 已改用 macOS 系统级热键注册,不需要“输入监控”权限。应用提供常驻控制台,也可以直接点击“开始结合屏幕 / 结束并提交”。
|
||||
|
||||
## 验收
|
||||
|
||||
1. 将光标放到 Codex 输入框。
|
||||
2. 按 `Command+1`,应出现“结合屏幕”提示。
|
||||
3. 说:`结合屏幕,这个页面有什么问题。`
|
||||
4. 再按 `Command+1`。
|
||||
5. 输入框应出现 `【卡若AI·结合屏幕】`、截图图片、截图路径和洗字后的语音指令。
|
||||
|
||||
截图和本次上下文包保存在:
|
||||
|
||||
`/Users/karuo/Documents/个人/卡若AI/02_卡人(水)/水溪_整理归档/语音输入/截图归档/`(仅保留最新100张截图;截图超出时只清理最早图片,上下文 `.md` 永久保留)
|
||||
|
||||
## 维护与回滚
|
||||
|
||||
- 可见应用:`/Applications/卡若AI·屏语.app`
|
||||
- 热键守护:`com.karuo.voice-screen-hotkey`
|
||||
- Handy 钩子:`~/Library/Application Support/com.pais.handy/hooks/transcription`
|
||||
- 重新部署:
|
||||
|
||||
```bash
|
||||
bash "/Users/karuo/Documents/个人/卡若AI/02_卡人(水)/水溪_整理归档/语音输入/脚本/部署结合屏幕.sh"
|
||||
```
|
||||
|
||||
- 停用:卸载 `com.karuo.voice-screen-hotkey` 并把 Handy 钩子改回 `01_卡资(金)/金仓_存储备份/脚本/handy_transcription_hook.sh`。
|
||||
@@ -25,7 +25,7 @@
|
||||
## 接线(推荐 · macOS)
|
||||
|
||||
```bash
|
||||
bash "/Users/karuo/Documents/个人/卡若AI/01_卡资(金)/金仓_存储备份/Handy中文语音/脚本/setup_handy_voice_exec_wake.sh"
|
||||
bash "/Users/karuo/Documents/个人/卡若AI/02_卡人(水)/水溪_整理归档/语音输入/脚本/setup_handy_voice_exec_wake.sh"
|
||||
```
|
||||
|
||||
使用 `hooks/transcription` + **stdout 空**,不启用 `external_script`(避免 0.8.3 回滚英文)。
|
||||
@@ -47,7 +47,7 @@
|
||||
## 接线(不改 Handy 设置)
|
||||
|
||||
```bash
|
||||
bash "/Users/karuo/Documents/个人/卡若AI/01_卡资(金)/金仓_存储备份/Handy中文语音/脚本/setup_handy_voice_exec_wake.sh"
|
||||
bash "/Users/karuo/Documents/个人/卡若AI/02_卡人(水)/水溪_整理归档/语音输入/脚本/setup_handy_voice_exec_wake.sh"
|
||||
```
|
||||
|
||||
**禁止**为接线而运行 `handy_apply_chinese_macos.sh`(会覆盖你的快捷键/语言等设置)。
|
||||
219
02_卡人(水)/水溪_整理归档/语音输入/开发文档/功能迭代记录.md
Normal file
219
02_卡人(水)/水溪_整理归档/语音输入/开发文档/功能迭代记录.md
Normal file
@@ -0,0 +1,219 @@
|
||||
# 功能迭代记录
|
||||
|
||||
## 2026-07-12|v1.4.3 修复自动输入
|
||||
|
||||
- 用户真实验证发现:纠错结果进剪贴板,但仍需手动粘贴。
|
||||
- 直接核对 Handy v0.8.3 源码 `src-tauri/src/clipboard.rs`:ExternalScript 将文本作为 `$1`,忽略 stdout,并要求脚本自行完成输入。
|
||||
- 插件恢复 `$1` 读取,纠错后写剪贴板,等待 350ms 恢复焦点,再自动发送 `⌘V`。
|
||||
- `clipboard_handling` 改为 `dont_modify`,避免 Handy 随后用原始转写覆盖纠错后的剪贴板。
|
||||
- 新增 `~/Library/Logs/com.pais.handy/karuo_asr_plugin.log`,记录调用、纠错和粘贴失败,不记录正文。
|
||||
- 自动输入优先使用本机 `cliclick`,不可用时才回退 System Events;macOS 必须授予对应输入程序辅助功能权限。
|
||||
|
||||
## 2026-07-12|v1.4.2 自动输入当前对话框
|
||||
|
||||
- 固化 `clipboard_handling=copy_to_clipboard` 与 `auto_submit=false`。
|
||||
- Handy 收到插件 stdout 后自动写入当前焦点对话框,不需要用户手动粘贴。
|
||||
- 默认只填入、不自动发送,避免语音误识别直接提交。
|
||||
|
||||
## 2026-07-12|v1.4.1 修复Handy插件传参协议
|
||||
|
||||
- 用户真实语音验证发现:Handy日志显示插件被调用,但输入框仍是原文。
|
||||
- 根因:旧脚本从参数/剪贴板读取,并自行 ⌘V;Handy 0.8.3 实际从 stdin 传入转写、读取 stdout 作为最终文本。
|
||||
- 修复:插件改为纯过滤器 `stdin → 回廊洗字 → stdout`,不再操作剪贴板或模拟按键。
|
||||
- 验收标准:管道输入“纯客宝、卡罗亚”时,stdout 必须返回“存客宝、卡若AI”。
|
||||
|
||||
## 2026-07-12|v1.4 Handy输入阶段ASR纠错
|
||||
|
||||
### 目标
|
||||
|
||||
Handy 语音转写后、文字进入当前输入框前,自动调用卡若AI统一ASR纠错库。
|
||||
|
||||
### 链路
|
||||
|
||||
```text
|
||||
Handy录音
|
||||
-> SenseVoice zh-Hans转写
|
||||
-> handy_macos_asr_paste.sh
|
||||
-> apply_karuo_voice_corrections.py
|
||||
-> 卡若闽南口音_ASR纠错库.json
|
||||
-> 剪贴板
|
||||
-> 当前输入框
|
||||
```
|
||||
|
||||
### 修改
|
||||
|
||||
- Handy `paste_method` 改为 `external_script`。
|
||||
- `external_script_path` 指向已验证的 `handy_macos_asr_paste.sh`。
|
||||
- 新增 `enable_karuo_asr_correction_macos.sh`,自动备份配置、接线并重启 Handy。
|
||||
- 钩子新增 `KARUO_ASR_TEST_ONLY=1` 无粘贴测试模式。
|
||||
- 补充 `卡罗AI/卡洛AI/卡路AI` 无空格纠错变体。
|
||||
|
||||
### 验证
|
||||
|
||||
- Handy 0.8.3 正常启动,SenseVoice 与 `zh-Hans` 保持不变。
|
||||
- Handy 日志确认加载 `ExternalScript` 和目标脚本。
|
||||
- 测试输入“打开纯客宝后台,卡罗AI帮我检查”输出“打开存客宝后台,卡若AI帮我检查”。
|
||||
- Cursor/Codex 对话层仍保留回廊洗字二次兜底。
|
||||
|
||||
### 回滚
|
||||
|
||||
恢复 `settings_store.json.bak.asr.*` 备份并重启 Handy,或把 `paste_method` 改回 `ctrl_v`、`external_script_path` 改回 `null`。
|
||||
|
||||
## 2026-07-26|v1.4.4 本机语音链路复核与 KI 沉淀
|
||||
|
||||
- **实测结论**:Handy v0.8.3 + 内置麦克风 + SenseVoice 中文可正常识别;日志显示最近转写耗时 90–471ms,自动粘贴成功。
|
||||
- **发现并修复**:实际运行配置遗留为 `Fn`、`push_to_talk=false`、`autostart_enabled=true`、未启用回廊洗字守护,导致“卡路AI / 卡罗AI”等误听原样落入对话。
|
||||
- **已统一基线**:`⌘1`、`zh-Hans`、`sense-voice-int8`、`push_to_talk=true`、`ctrl_v`、`copy_to_clipboard`、`auto_submit=false`、`autostart_enabled=false`;已安装 `com.karuo.handy-asr-wash`。
|
||||
- **验证**:`test_karuo_asr_corrections.py` 3/3 通过;纠错示例“纯客宝、卡罗AI、流量词”→“存客宝、卡若AI、流量池”。
|
||||
- **已知约束**:Handy 0.8.3 启动后会把 `ort_accelerator` 归一成 `auto`;当前识别正常,未升级到 0.9.4,待隔离兼容性验证后再决定。
|
||||
- **知识沉淀**:[Handy语音输入_KI_本机实测_20260726.md](../references/Handy语音输入_KI_本机实测_20260726.md)。
|
||||
|
||||
## 2026-07-26|v1.4.5 按用户确认恢复原始 Handy 配置
|
||||
|
||||
- 已恢复并由启动日志确认:热键 `Fn`、`push_to_talk=false`(无需按住)、`autostart_enabled=true`、`CtrlV` 自动填入。
|
||||
- 已卸载本轮新增 `com.karuo.handy-asr-wash`,不再在输入前额外修改剪贴板。
|
||||
- 后续以用户确认的原配置为唯一基线;修改前先备份并明确说明改动项。
|
||||
|
||||
## 2026-07-26|v1.5 能力包迁移至卡水·语音输入
|
||||
|
||||
- 按用户确认,原 `金仓/Handy中文语音` **整体迁移并重命名**为 `水溪/语音输入`,未创建并行重复 Skill。
|
||||
- Skill 元数据统一为:`name=语音输入`、`group=水`、`owner=水溪`、`编号=W03h`。
|
||||
- 路由职责已拆分:W03h 管日常语音输入、配置、验收与排障;W03b「回廊洗字」只管 ASR 词库和语义纠错。
|
||||
- 当前用户确认的电脑基线:`Fn` 开始录音、再按 `Fn` 结束,`push_to_talk=false`,不安装剪贴板洗字守护。
|
||||
|
||||
## 2026-07-26|HeyClicky 调研与 W03h 优化方案
|
||||
|
||||
- 调研 [farzaa/clicky](https://github.com/farzaa/clicky) 与 [heyclicky 官网](https://www.heyclicky.com/)。可借鉴核心是“听写 / 屏幕上下文问答 / Agent 任务”三条独立链路。
|
||||
- 卡若AI采用增量方案:保留当前 `Fn` 切换录音、Handy、SenseVoice 和 W03b 洗字;P0 先建立本地语音意图分流与结构化回执,不改 Handy 配置。
|
||||
- 完整方案:[heyclicky调研与语音输入迭代方案_20260726.md](../references/heyclicky调研与语音输入迭代方案_20260726.md)。
|
||||
|
||||
## 2026-07-26|v1.6 Home+1「结合屏幕」本地模式
|
||||
|
||||
- 新增本地 `Home+1` 热键守护:首次按键单次截取主屏并启动 Handy 后处理录音;再次按键结束。
|
||||
- 新增转写钩子:将截图绝对路径、W03b 洗字结果和语音指令组装为上下文包,填入当前 AI 输入框。
|
||||
- `Fn`、`push_to_talk=false` 未改动;结合屏幕只在 `Home+1` 下工作,无常驻截屏。
|
||||
- 运行服务 `com.karuo.voice-screen-hotkey` 已加载;首次使用需授予 macOS 输入监控权限。
|
||||
- 使用说明:[结合屏幕本地部署与使用.md](../references/结合屏幕本地部署与使用.md)。
|
||||
|
||||
## 2026-07-26|v1.6.1 结合屏幕热键改为 Command+1
|
||||
|
||||
- 按用户指定,热键从 Home+1 切换为 `Command+1`;本地 LaunchAgent 已重编译并重启。
|
||||
- `Fn` 普通语音输入与“无需按住说话”保持不变。
|
||||
- 已通过命令直达 macOS「输入监控」授权页;该系统级权限由当前登录用户最后确认一次。
|
||||
|
||||
|
||||
## 2026-07-26|v1.6.2 可见应用与输入监控修复
|
||||
|
||||
- 根因:原热键守护是无界面二进制,macOS「输入监控」列表不会显示可选择项目。
|
||||
- 已改为可见的 `/Applications/卡若AI结合屏幕.app`(bundle id `com.karuo.voice-screen`),菜单栏显示“屏幕语音”,包含“打开输入监控设置”和“测试截图”。
|
||||
- LaunchAgent 已改为启动该应用包内可执行文件;部署脚本同步改为应用化安装与注册。
|
||||
- 保持不变:`Fn` 普通语音、`push_to_talk=false`、Handy 原始配置。
|
||||
|
||||
|
||||
## 2026-07-26|v1.6.3 卡若AI·屏语命名与本地截图归档
|
||||
|
||||
- 应用重命名为 **卡若AI·屏语**:语义为“屏幕 + 语音”,与卡若AI产品命名统一,菜单栏也显示该名称。
|
||||
- 截图归档改至 `02_卡人(水)/水溪_整理归档/语音输入/截图归档/`,即卡若AI主目录内。
|
||||
- 新增上限:仅保留最新 **100 张** `screen-*.png`;生成第101张时自动删除最早截图及同名 `.md` 上下文文件。
|
||||
- 归档文件被 `.gitignore` 排除,仅本机保留,不同步到版本库。
|
||||
|
||||
|
||||
## 2026-07-26|v1.6.4 屏语截图清理与上下文留存分离
|
||||
|
||||
- 按用户确认,超出100张时**只删除最早 `screen-*.png` 截图**。
|
||||
- 同名 `.md` 语音上下文包不再随图片清理,作为本地历史记录持续保留。
|
||||
|
||||
|
||||
## 2026-07-26|v1.6.5 卡若AI·屏语原生应用图标
|
||||
|
||||
- 新增“水感屏幕 + 声波”应用图标源文件:`资源/卡若AI·屏语.png`。
|
||||
- 部署脚本自动生成 macOS `.icns` 并写入 App Bundle,Finder、Dock 与输入监控均使用同一图标。
|
||||
|
||||
|
||||
## 2026-07-26|v1.6.6 屏语可视状态与直接输出
|
||||
|
||||
- 新增菜单入口、浮动状态窗与完整状态:正在截图 → 正在说话 → 正在转写并提交 → 当前 AI 正在回答。
|
||||
- `Command+1` 与菜单均可启动/结束;若截屏权限缺失,应用自动打开“屏幕录制”设置。
|
||||
- 屏语专用转写通过 Handy ExternalScript 自动提交给当前 AI;普通 `Fn` 仍按一次开始、再按一次结束,仍然只填入不自动发送。
|
||||
|
||||
|
||||
## 2026-07-26|v1.6.7 屏语控制台与系统级快捷键
|
||||
|
||||
- 根因修复:原 `Command+1` 全局监听依赖输入监控且无授权记录,已改为 Carbon `RegisterEventHotKey` 系统级注册,不再依赖输入监控。
|
||||
- 新增常驻屏语控制台:开始结合屏幕、结束并提交、开启录屏权限三个直接按钮。
|
||||
- 状态窗改为非激活浮窗,不抢走当前 AI 输入框焦点,避免转写结果粘贴到错误位置。
|
||||
- 首次唯一权限为“录屏与系统录音”;权限关闭时控制台明确提示并直达设置页。
|
||||
|
||||
|
||||
## 2026-07-26|v1.6.8 移除录屏权限误判
|
||||
|
||||
- 用户截图确认“卡若AI·屏语”录屏开关已开启;旧版 `CGPreflightScreenCaptureAccess()` 在当前 LaunchAgent 启动上下文返回假阴性。
|
||||
- 已移除启动前权限预检,改为直接执行真实截图,以截图文件和返回码作为唯一成功标准。
|
||||
- 控制台不再错误提示重复开启权限。
|
||||
|
||||
|
||||
## 2026-07-26|v1.6.9 原生进程截图
|
||||
|
||||
- 实测确认录屏开关已开启,但 `/usr/sbin/screencapture` 子进程未继承应用 TCC 身份。
|
||||
- 截图改由 `卡若AI·屏语` Swift 主进程通过 CoreGraphics 直接生成,再使用 `--arm-existing` 登记并启动 Handy。
|
||||
- 权限归属、界面状态与截图执行统一到同一 App Bundle。
|
||||
|
||||
|
||||
## 2026-07-26|v1.6.10 LaunchServices应用身份启动
|
||||
|
||||
- 根因:LaunchAgent 直接执行 App 包内二进制,TCC录屏权限虽然界面开启,但运行进程未按 Bundle 身份归属。
|
||||
- LaunchAgent 改为 `/usr/bin/open -g /Applications/卡若AI·屏语.app`,由 LaunchServices 以完整 Bundle ID 启动。
|
||||
|
||||
|
||||
## 2026-07-26|v1.6.11 本地截图助手兜底
|
||||
|
||||
- 系统设置显示录屏已开启,但ScreenCaptureKit返回TCC拒绝(设置界面与TCC内部状态冲突)。
|
||||
- 新增 `屏语截图助手.py`,经本机已授权执行链路实测截图成功;主应用不再显示重复授权提示。
|
||||
- 屏语按钮统一通过助手生成截图,再进入Handy录音与自动提交。
|
||||
|
||||
|
||||
## 2026-07-26|v1.6.12 运行时与用户归档分离
|
||||
|
||||
- 修复App子进程读取Documents脚本时阻塞:运行脚本和IPC迁至 `~/Library/Application Support/karuo-ai/pingyu/`。
|
||||
- 截图与上下文仍保存到卡若AI `语音输入/截图归档/`,用户目录结构不变。
|
||||
|
||||
|
||||
## 2026-07-26|v1.6.13 屏语只粘贴不自动发送
|
||||
|
||||
- 按用户确认,屏语结束后仅把“截图路径 + 洗字语音指令”粘贴到当前输入框。
|
||||
- 移除 `--submit` 调用,不再模拟回车;发送动作始终由用户确认。
|
||||
- 控制台文案统一改为“结束并粘贴 / 已粘贴到输入框”。
|
||||
|
||||
|
||||
## 2026-07-26|v1.6.14 Handy式无弹窗交互
|
||||
|
||||
- 移除屏语启动、截图、说话、处理、粘贴过程中的浮动控制窗。
|
||||
- 状态仅显示在菜单栏:`屏语·截图中 → 屏语·说话中 → 屏语·处理中 → 屏语·已粘贴`。
|
||||
- 操作保持 `Command+1` 开始、再次 `Command+1` 结束并只粘贴,不自动回车。
|
||||
|
||||
|
||||
## 2026-07-26|v1.6.15 真实图片附件粘贴
|
||||
|
||||
- 屏语结束后不再只粘贴图片路径;新增 `屏语图片粘贴.swift`,将 PNG 图片和上下文文字一起写入剪贴板。
|
||||
- 当前 AI 输入框会显示真实截图附件;仍然只粘贴、不自动回车。
|
||||
|
||||
## v1.6.16 真实图片附件+语音文字同次粘贴(2026-07-26)
|
||||
- **目标**:Command+1 流程不再只粘贴截图路径,而是将真实 PNG 图片与洗字后的语音文字一次写入当前 AI 输入框;保持不自动回车。
|
||||
- **实现**:截图助手把归档 PNG 同步到 `$HOME/Library/Application Support/karuo-ai/pingyu/latest-screen.png`;`屏语图片粘贴.swift` 通过 NSPasteboard 同时登记 `PNGf` 与 `string`,再仅模拟 Command+V。
|
||||
- **验证**:`osascript -e 'clipboard info'` 已显示 `«class PNGf»` 与 `«class utf8»`;`结合屏幕.py --consume --paste` 返回 0,pending 状态已清理。
|
||||
- **架构**:Command+1 → 截图助手 → runtime 图片副本 → Handy 转写 → 图片+文字剪贴板 → 当前输入框;上下文 `.md` 与归档图片继续保留。
|
||||
|
||||
## v1.6.17 双粘贴链路分流(2026-07-26)
|
||||
- **Fn/Handy**:恢复 `paste_method=ctrl_v`、`external_script_path=null`,保持原生语音文字粘贴。
|
||||
- **Command+1/屏语**:屏语应用记录录音前剪贴板版本,结束时等待 Handy 原生转写完成,再单独调用 `结合屏幕.py --consume --paste`,将 PNG+文字一次粘贴,不改变 Fn 链路。
|
||||
- **验证**:应用已重新编译签名并启动;配置读取为 `ctrl_v / null`;两种模式互不调用同一外部钩子。
|
||||
|
||||
## v1.6.18 屏语分步附件粘贴(2026-07-26)
|
||||
- **问题**:部分 AI 输入框对同一剪贴板中的 PNG+文字只取文字,用户看不到图片。
|
||||
- **修复**:屏语助手改为同一任务内先粘贴 PNG 图片,再粘贴语音文字;Fn/Handy 仍只走原生文字 `ctrl_v`。
|
||||
- **验证**:运行目录助手编译成功;`clipboard info` 在图片阶段显示 `PNGf`,无自动回车。
|
||||
|
||||
## v1.6.19 输入焦点恢复(2026-07-26)
|
||||
- **问题**:Command+1 结束录音后,系统焦点可能仍停留在 Handy/屏语状态栏,导致图片粘贴位置不可见。
|
||||
- **修复**:开始屏语时记录当前前台输入应用;收到 Handy 转写后先恢复该应用焦点,再执行“图片→文字”两段粘贴。
|
||||
- **验证**:最近日志已持续记录截图生成与转写消费;新版本应用已重新编译、签名并启动。
|
||||
3
02_卡人(水)/水溪_整理归档/语音输入/截图归档/.gitignore
vendored
Normal file
3
02_卡人(水)/水溪_整理归档/语音输入/截图归档/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# 本机屏语截图与上下文不进入版本库
|
||||
*
|
||||
!.gitignore
|
||||
@@ -48,7 +48,7 @@ chmod +x "${PKG_DIR}/脚本/install_all.sh"
|
||||
cat > "${PKG_DIR}/卡若AI技能路径.txt" << EOF
|
||||
本包对应卡若AI Skill(安装后 Agent 自动识别):
|
||||
|
||||
/Users/karuo/Documents/个人/卡若AI/01_卡资(金)/金仓_存储备份/Handy中文语音/SKILL.md
|
||||
/Users/karuo/Documents/个人/卡若AI/02_卡人(水)/水溪_整理归档/语音输入/SKILL.md
|
||||
|
||||
编号:G25b · 金仓 · Handy中文语音
|
||||
关联:回廊洗字 W03b(Cursor 内 ASR 纠错)
|
||||
@@ -99,7 +99,7 @@ cat > "${TOOL_ROOT}/README.md" << 'TOOLREADME'
|
||||
| **Windows** | `Handy中文语音-安装包-Windows-v1.1/` → `双击安装Handy中文语音.bat` | `Handy中文语音_Windows_安装包_YYYYMMDD.zip` |
|
||||
| **语音唤 Agent** | 包内 `语音唤醒卡若AI.md` + `setup_voice_agent_plugin.*` | 说「卡若AI …」或「火炬 …」 |
|
||||
|
||||
卡若AI Skill:`卡若AI/01_卡资(金)/金仓_存储备份/Handy中文语音/SKILL.md`(G25b)
|
||||
卡若AI Skill:`卡若AI/02_卡人(水)/水溪_整理归档/语音输入/SKILL.md`(G25b)
|
||||
|
||||
归类:`4、火_工具开发/工具/`(黑曜石10_归类总册 · tool_release)
|
||||
TOOLREADME
|
||||
@@ -53,7 +53,7 @@ PS1
|
||||
cat > "${PKG_DIR}/卡若AI技能路径.txt" << EOF
|
||||
卡若AI Skill(G25b · Handy中文语音):
|
||||
|
||||
/Users/karuo/Documents/个人/卡若AI/01_卡资(金)/金仓_存储备份/Handy中文语音/SKILL.md
|
||||
/Users/karuo/Documents/个人/卡若AI/02_卡人(水)/水溪_整理归档/语音输入/SKILL.md
|
||||
|
||||
Windows 安装包目录(五行 · 火 · 工具):
|
||||
${TOOL_ROOT}/${PKG_NAME}/
|
||||
12
02_卡人(水)/水溪_整理归档/语音输入/脚本/handy_结合屏幕钩子.sh
Executable file
12
02_卡人(水)/水溪_整理归档/语音输入/脚本/handy_结合屏幕钩子.sh
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
# Handy 0.8.3 ExternalScript:屏语已武装则提交屏幕任务;否则完全沿用原 Fn 输入脚本。
|
||||
set -euo pipefail
|
||||
ROOT="/Users/karuo/Documents/个人/卡若AI"
|
||||
SCREEN="$HOME/Library/Application Support/karuo-ai/pingyu/结合屏幕.py"
|
||||
LEGACY="$ROOT/01_卡资(金)/金仓_存储备份/脚本/handy_macos_asr_paste.sh"
|
||||
RAW="${1:-}"
|
||||
if [[ -z "${RAW//[[:space:]]/}" ]]; then RAW="$(cat)"; fi
|
||||
if [[ -n "${RAW//[[:space:]]/}" ]] && python3 "$SCREEN" --consume --paste "$RAW" >/dev/null 2>&1; then
|
||||
exit 0
|
||||
fi
|
||||
exec "$LEGACY" "$RAW"
|
||||
32
02_卡人(水)/水溪_整理归档/语音输入/脚本/屏语图片粘贴.swift
Normal file
32
02_卡人(水)/水溪_整理归档/语音输入/脚本/屏语图片粘贴.swift
Normal file
@@ -0,0 +1,32 @@
|
||||
import AppKit
|
||||
import Foundation
|
||||
|
||||
let args = CommandLine.arguments
|
||||
let copyOnly = args.contains("--copy-only")
|
||||
let positional = args.dropFirst().filter { $0 != "--copy-only" }
|
||||
guard positional.count >= 2 else { exit(2) }
|
||||
let imagePath = String(positional[0])
|
||||
let text = String(positional[1])
|
||||
guard let imageData = try? Data(contentsOf: URL(fileURLWithPath: imagePath)),
|
||||
imageData.count > 8 else { exit(3) }
|
||||
let png = imageData
|
||||
let pasteboard = NSPasteboard.general
|
||||
pasteboard.clearContents()
|
||||
pasteboard.declareTypes([.png], owner: nil)
|
||||
guard pasteboard.setData(png, forType: .png) else { exit(4) }
|
||||
if !copyOnly {
|
||||
usleep(350_000)
|
||||
func paste() {
|
||||
let task = Process()
|
||||
task.executableURL = URL(fileURLWithPath: "/usr/bin/osascript")
|
||||
task.arguments = ["-e", "tell application \"System Events\" to keystroke \"v\" using command down"]
|
||||
try? task.run(); task.waitUntilExit()
|
||||
}
|
||||
paste()
|
||||
usleep(350_000)
|
||||
pasteboard.clearContents()
|
||||
pasteboard.declareTypes([.string], owner: nil)
|
||||
guard pasteboard.setString(text, forType: .string) else { exit(5) }
|
||||
usleep(180_000)
|
||||
paste()
|
||||
}
|
||||
27
02_卡人(水)/水溪_整理归档/语音输入/脚本/屏语截图助手.py
Executable file
27
02_卡人(水)/水溪_整理归档/语音输入/脚本/屏语截图助手.py
Executable file
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env python3
|
||||
import json, os, subprocess, time
|
||||
from pathlib import Path
|
||||
STATE = Path.home() / 'Library/Application Support/karuo-ai/pingyu'
|
||||
REQUEST = STATE / 'capture-request.json'
|
||||
RESPONSE = STATE / 'capture-response.json'
|
||||
PID = STATE / 'capture-helper.pid'
|
||||
STATE.mkdir(parents=True, exist_ok=True)
|
||||
PID.write_text(str(os.getpid()))
|
||||
while True:
|
||||
if REQUEST.exists():
|
||||
try:
|
||||
data=json.loads(REQUEST.read_text())
|
||||
output=Path(data['output'])
|
||||
REQUEST.unlink(missing_ok=True)
|
||||
result=subprocess.run(['/usr/sbin/screencapture','-x','-m',str(output)],capture_output=True,text=True)
|
||||
ok=result.returncode == 0 and output.exists() and output.stat().st_size > 0
|
||||
if ok:
|
||||
# 将图片同步到应用支持目录,供粘贴助手读取,避免 Documents 的 TCC 读取差异。
|
||||
runtime_image = STATE / 'latest-screen.png'
|
||||
runtime_image.write_bytes(output.read_bytes())
|
||||
shots=sorted(output.parent.glob("screen-*.png"), key=lambda item:item.stat().st_mtime, reverse=True)
|
||||
for old in shots[100:]: old.unlink(missing_ok=True)
|
||||
RESPONSE.write_text(json.dumps({'ok':ok,'output':str(output),'runtime_output':str(STATE / 'latest-screen.png') if ok else '', 'error':result.stderr.strip()},ensure_ascii=False))
|
||||
except Exception as exc:
|
||||
RESPONSE.write_text(json.dumps({'ok':False,'error':str(exc)},ensure_ascii=False))
|
||||
time.sleep(0.12)
|
||||
184
02_卡人(水)/水溪_整理归档/语音输入/脚本/结合屏幕.py
Executable file
184
02_卡人(水)/水溪_整理归档/语音输入/脚本/结合屏幕.py
Executable file
@@ -0,0 +1,184 @@
|
||||
#!/usr/bin/env python3
|
||||
"""卡若AI W03h:Command+1 结合屏幕本地上下文包。"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path("/Users/karuo/Documents/个人/卡若AI")
|
||||
ARCHIVE_DIR = ROOT / "02_卡人(水)/水溪_整理归档/语音输入/截图归档"
|
||||
STATE_DIR = Path.home() / "Library/Application Support/karuo-ai/pingyu"
|
||||
MAX_SCREENSHOTS = 100
|
||||
PENDING = STATE_DIR / "pending.json"
|
||||
LOG = STATE_DIR / "screen-context.log"
|
||||
HANDY = Path("/Applications/Handy.app/Contents/MacOS/handy")
|
||||
CAPTURE_REQUEST = STATE_DIR / "capture-request.json"
|
||||
CAPTURE_RESPONSE = STATE_DIR / "capture-response.json"
|
||||
CORRECTION_SCRIPT = ROOT / "运营中枢/工作台/脚本/apply_karuo_voice_corrections.py"
|
||||
PASTE_HELPER = Path.home() / "Library/Application Support/karuo-ai/pingyu/屏语图片粘贴"
|
||||
|
||||
|
||||
def write_log(event: str) -> None:
|
||||
STATE_DIR.mkdir(parents=True, exist_ok=True)
|
||||
with LOG.open("a", encoding="utf-8") as f:
|
||||
f.write(f"{datetime.now().isoformat(timespec='seconds')} {event}\n")
|
||||
|
||||
|
||||
def cleanup_screenshots() -> int:
|
||||
"""只保留最新 100 张屏语截图;上下文记录永久保留。"""
|
||||
shots = sorted(ARCHIVE_DIR.glob("screen-*.png"), key=lambda item: item.stat().st_mtime, reverse=True)
|
||||
removed = 0
|
||||
for shot in shots[MAX_SCREENSHOTS:]:
|
||||
shot.unlink(missing_ok=True)
|
||||
removed += 1
|
||||
if removed:
|
||||
write_log(f"cleanup removed={removed} remaining={min(len(shots), MAX_SCREENSHOTS)}")
|
||||
return removed
|
||||
|
||||
|
||||
def read_pending() -> dict | None:
|
||||
try:
|
||||
data = json.loads(PENDING.read_text(encoding="utf-8"))
|
||||
if time.time() > float(data.get("expires_at", 0)):
|
||||
PENDING.unlink(missing_ok=True)
|
||||
write_log("expired")
|
||||
return None
|
||||
return data
|
||||
except (OSError, ValueError, TypeError):
|
||||
return None
|
||||
|
||||
|
||||
def correct(text: str) -> str:
|
||||
if not CORRECTION_SCRIPT.exists():
|
||||
return text.strip()
|
||||
out = subprocess.run(
|
||||
[sys.executable, str(CORRECTION_SCRIPT), text],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
return (out.stdout or text).strip()
|
||||
|
||||
|
||||
def register_screenshot(shot: Path, *, start_recording: bool) -> int:
|
||||
if not shot.exists() or shot.stat().st_size == 0:
|
||||
write_log(f"existing-capture-invalid path={shot}")
|
||||
return 2
|
||||
PENDING.write_text(
|
||||
json.dumps({"screenshot": str(shot), "armed_at": time.time(), "expires_at": time.time() + 180}, ensure_ascii=False),
|
||||
encoding="utf-8",
|
||||
)
|
||||
write_log(f"armed screenshot={shot.name}")
|
||||
if start_recording and HANDY.exists():
|
||||
subprocess.Popen([str(HANDY), "--toggle-post-process"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
return 0
|
||||
|
||||
|
||||
def arm_existing(path: str, *, start_recording: bool) -> int:
|
||||
STATE_DIR.mkdir(parents=True, exist_ok=True)
|
||||
if read_pending():
|
||||
write_log("existing-capture-pending-conflict")
|
||||
return 4
|
||||
return register_screenshot(Path(path), start_recording=start_recording)
|
||||
|
||||
|
||||
def arm(*, start_recording: bool) -> int:
|
||||
STATE_DIR.mkdir(parents=True, exist_ok=True)
|
||||
current = read_pending()
|
||||
if current:
|
||||
if start_recording and HANDY.exists():
|
||||
subprocess.Popen([str(HANDY), "--toggle-post-process"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
write_log("toggle-stop")
|
||||
return 0
|
||||
|
||||
ARCHIVE_DIR.mkdir(parents=True, exist_ok=True)
|
||||
shot = ARCHIVE_DIR / f"screen-{datetime.now():%Y%m%d-%H%M%S}.png"
|
||||
CAPTURE_RESPONSE.unlink(missing_ok=True)
|
||||
CAPTURE_REQUEST.write_text(json.dumps({"output": str(shot)}, ensure_ascii=False), encoding="utf-8")
|
||||
deadline = time.time() + 5
|
||||
while time.time() < deadline and not CAPTURE_RESPONSE.exists():
|
||||
time.sleep(0.1)
|
||||
try:
|
||||
response = json.loads(CAPTURE_RESPONSE.read_text(encoding="utf-8"))
|
||||
except (OSError, ValueError):
|
||||
response = {"ok": False, "error": "helper-timeout"}
|
||||
if not response.get("ok"):
|
||||
write_log(f"capture-helper-failed error={response.get('error', '')}")
|
||||
return 2
|
||||
result = register_screenshot(shot, start_recording=start_recording)
|
||||
if result == 0 and response.get("runtime_output"):
|
||||
state = read_pending()
|
||||
if state:
|
||||
state["runtime_screenshot"] = response["runtime_output"]
|
||||
PENDING.write_text(json.dumps(state, ensure_ascii=False), encoding="utf-8")
|
||||
return result
|
||||
|
||||
|
||||
def consume(raw: str, *, paste: bool, submit: bool, dry_run: bool) -> int:
|
||||
state = read_pending()
|
||||
if not state:
|
||||
return 3
|
||||
corrected = correct(raw)
|
||||
task = corrected.removeprefix("结合屏幕").lstrip(",,:: ") or corrected
|
||||
screenshot = Path(str(state["screenshot"]))
|
||||
attachment = Path(str(state.get("runtime_screenshot", screenshot)))
|
||||
packet = (
|
||||
"【卡若AI·结合屏幕】\n"
|
||||
f"请查看当前屏幕截图:{screenshot}\n"
|
||||
"截图只用于本次上下文,不做常驻监控。\n"
|
||||
f"语音指令(已洗字):{task}\n"
|
||||
"请先结合截图理解,再直接回答或执行。"
|
||||
)
|
||||
context = screenshot.with_suffix(".md")
|
||||
context.write_text(packet + "\n", encoding="utf-8")
|
||||
PENDING.unlink(missing_ok=True)
|
||||
write_log(f"consumed screenshot={screenshot.name} raw_len={len(raw)}")
|
||||
if paste and not dry_run:
|
||||
if PASTE_HELPER.exists() and attachment.exists():
|
||||
subprocess.run([str(PASTE_HELPER), str(attachment), packet], check=False)
|
||||
else:
|
||||
subprocess.run(["pbcopy"], input=packet, text=True, check=False)
|
||||
time.sleep(0.35)
|
||||
subprocess.run(["osascript", "-e", 'tell application "System Events" to keystroke "v" using command down'], check=False)
|
||||
# 屏语始终不自动回车;submit 参数保留作兼容但不执行。
|
||||
print(packet)
|
||||
return 0
|
||||
|
||||
|
||||
def main() -> int:
|
||||
ap = argparse.ArgumentParser(description="卡若AI结合屏幕本地上下文")
|
||||
group = ap.add_mutually_exclusive_group(required=True)
|
||||
group.add_argument("--toggle", action="store_true", help="Command+1 调用:截图并开始/结束后处理录音")
|
||||
group.add_argument("--arm", action="store_true", help="仅截图武装,供测试")
|
||||
group.add_argument("--arm-existing", metavar="PATH", help="登记由屏语应用原生生成的截图并开始录音")
|
||||
group.add_argument("--consume", action="store_true", help="消费待处理的转写")
|
||||
group.add_argument("--status", action="store_true", help="显示屏语截图归档状态")
|
||||
ap.add_argument("--paste", action="store_true", help="消费后填入当前输入框")
|
||||
ap.add_argument("--submit", action="store_true", help="仅屏语模式:填入后自动提交给当前 AI")
|
||||
ap.add_argument("--dry-run", action="store_true", help="不触发录音或粘贴")
|
||||
ap.add_argument("text", nargs="?", help="转写文本;缺省时从 stdin 读取")
|
||||
args = ap.parse_args()
|
||||
if args.status:
|
||||
STATE_DIR.mkdir(parents=True, exist_ok=True)
|
||||
removed = cleanup_screenshots()
|
||||
count = len(list(ARCHIVE_DIR.glob("screen-*.png")))
|
||||
print(json.dumps({"directory": str(ARCHIVE_DIR), "screenshots": count, "max_screenshots": MAX_SCREENSHOTS, "cleaned": removed}, ensure_ascii=False))
|
||||
return 0
|
||||
if args.toggle:
|
||||
return arm(start_recording=not args.dry_run)
|
||||
if args.arm:
|
||||
return arm(start_recording=False)
|
||||
if args.arm_existing:
|
||||
return arm_existing(args.arm_existing, start_recording=not args.dry_run)
|
||||
raw = args.text if args.text is not None else sys.stdin.read()
|
||||
return consume(raw, paste=args.paste, submit=args.submit, dry_run=args.dry_run)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
305
02_卡人(水)/水溪_整理归档/语音输入/脚本/结合屏幕应用.swift
Normal file
305
02_卡人(水)/水溪_整理归档/语音输入/脚本/结合屏幕应用.swift
Normal file
@@ -0,0 +1,305 @@
|
||||
import AppKit
|
||||
import ApplicationServices
|
||||
import Carbon.HIToolbox
|
||||
import CoreGraphics
|
||||
import Foundation
|
||||
import ScreenCaptureKit
|
||||
|
||||
let screenScript = NSHomeDirectory() + "/Library/Application Support/karuo-ai/pingyu/结合屏幕.py"
|
||||
let archiveDirectory = "/Users/karuo/Documents/个人/卡若AI/02_卡人(水)/水溪_整理归档/语音输入/截图归档"
|
||||
let pendingFile = NSHomeDirectory() + "/Library/Application Support/karuo-ai/pingyu/pending.json"
|
||||
|
||||
private let screenVoiceHotKeyHandler: EventHandlerUPP = { _, event, userData in
|
||||
guard let event, let userData else { return OSStatus(eventNotHandledErr) }
|
||||
var hotKeyID = EventHotKeyID()
|
||||
let status = GetEventParameter(
|
||||
event,
|
||||
EventParamName(kEventParamDirectObject),
|
||||
EventParamType(typeEventHotKeyID),
|
||||
nil,
|
||||
MemoryLayout<EventHotKeyID>.size,
|
||||
nil,
|
||||
&hotKeyID
|
||||
)
|
||||
guard status == noErr, hotKeyID.id == 1 else { return status }
|
||||
let controller = Unmanaged<ScreenVoiceApp>.fromOpaque(userData).takeUnretainedValue()
|
||||
DispatchQueue.main.async { controller.handleHotKey() }
|
||||
return noErr
|
||||
}
|
||||
|
||||
final class ScreenVoiceApp: NSObject, NSApplicationDelegate {
|
||||
private var statusItem: NSStatusItem!
|
||||
private var panel: NSPanel?
|
||||
private var titleLabel: NSTextField?
|
||||
private var detailLabel: NSTextField?
|
||||
private var startButton: NSButton?
|
||||
private var finishButton: NSButton?
|
||||
private var hotKeyRef: EventHotKeyRef?
|
||||
private var eventHandlerRef: EventHandlerRef?
|
||||
private var active = false
|
||||
private var clipboardChangeBeforeRecording: Int = 0
|
||||
private var targetApplication: NSRunningApplication?
|
||||
|
||||
func applicationDidFinishLaunching(_ notification: Notification) {
|
||||
NSApp.setActivationPolicy(.accessory)
|
||||
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
|
||||
statusItem.button?.title = "🎙屏语"
|
||||
statusItem.button?.toolTip = "卡若AI·屏语:Command + 1 开始或结束"
|
||||
rebuildMenu()
|
||||
installSystemHotKey()
|
||||
showReady()
|
||||
}
|
||||
|
||||
func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
|
||||
showReady()
|
||||
return true
|
||||
}
|
||||
|
||||
private func menuItem(_ title: String, _ action: Selector) -> NSMenuItem {
|
||||
let item = NSMenuItem(title: title, action: action, keyEquivalent: "")
|
||||
item.target = self
|
||||
return item
|
||||
}
|
||||
|
||||
private func rebuildMenu() {
|
||||
let menu = NSMenu()
|
||||
menu.addItem(menuItem("开始结合屏幕(⌘1)", #selector(start)))
|
||||
menu.addItem(menuItem("结束并粘贴(⌘1)", #selector(finish)))
|
||||
menu.addItem(NSMenuItem.separator())
|
||||
menu.addItem(menuItem("打开屏幕录制设置", #selector(openScreenRecording)))
|
||||
menu.addItem(NSMenuItem.separator())
|
||||
menu.addItem(menuItem("退出卡若AI·屏语", #selector(quit)))
|
||||
statusItem.menu = menu
|
||||
}
|
||||
|
||||
private func installSystemHotKey() {
|
||||
var eventType = EventTypeSpec(
|
||||
eventClass: OSType(kEventClassKeyboard),
|
||||
eventKind: UInt32(kEventHotKeyPressed)
|
||||
)
|
||||
InstallEventHandler(
|
||||
GetApplicationEventTarget(),
|
||||
screenVoiceHotKeyHandler,
|
||||
1,
|
||||
&eventType,
|
||||
Unmanaged.passUnretained(self).toOpaque(),
|
||||
&eventHandlerRef
|
||||
)
|
||||
let hotKeyID = EventHotKeyID(signature: OSType(0x4B525359), id: 1) // KRSY
|
||||
RegisterEventHotKey(
|
||||
UInt32(kVK_ANSI_1),
|
||||
UInt32(cmdKey),
|
||||
hotKeyID,
|
||||
GetApplicationEventTarget(),
|
||||
0,
|
||||
&hotKeyRef
|
||||
)
|
||||
}
|
||||
|
||||
func handleHotKey() {
|
||||
active ? finish() : start()
|
||||
}
|
||||
|
||||
private func showReady() {
|
||||
statusItem.button?.title = "🎙屏语"
|
||||
statusItem.button?.toolTip = "Command + 1 开始;再次按下结束并粘贴"
|
||||
panel?.orderOut(nil)
|
||||
}
|
||||
|
||||
@objc private func start() {
|
||||
guard !active else { finish(); return }
|
||||
targetApplication = NSWorkspace.shared.frontmostApplication
|
||||
clipboardChangeBeforeRecording = NSPasteboard.general.changeCount
|
||||
showState("正在截图…", "屏语正在获取当前画面,请稍等。")
|
||||
statusItem.button?.title = "🎙屏语·截图中"
|
||||
runPython(["--toggle"]) { code in
|
||||
DispatchQueue.main.async {
|
||||
if code == 0 {
|
||||
self.active = true
|
||||
self.statusItem.button?.title = "🎙屏语·说话中"
|
||||
self.updateButtons()
|
||||
self.showState("已截图,正在听你说话", "现在直接说需求;说完再按 Command + 1,系统只会粘贴到输入框,不会自动发送。")
|
||||
} else {
|
||||
self.statusItem.button?.title = "🎙屏语"
|
||||
self.showState("截图正在恢复", "本地截图助手正在重连,请再点击一次开始。")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func finish() {
|
||||
guard active else { start(); return }
|
||||
statusItem.button?.title = "🎙屏语·处理中"
|
||||
showState("正在转写并粘贴", "正在把截图和语音内容放入当前输入框,不会自动发送。")
|
||||
runPython(["--toggle"]) { _ in
|
||||
DispatchQueue.main.async { self.waitForHandyTranscript(remaining: 80) }
|
||||
}
|
||||
}
|
||||
|
||||
private func waitForHandyTranscript(remaining: Int) {
|
||||
let board = NSPasteboard.general
|
||||
if board.changeCount != clipboardChangeBeforeRecording,
|
||||
let transcript = board.string(forType: .string),
|
||||
!transcript.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
||||
targetApplication?.activate(options: [.activateIgnoringOtherApps, .activateAllWindows])
|
||||
usleep(220_000)
|
||||
runPythonWithInput(["--consume", "--paste"], input: transcript) { _ in
|
||||
DispatchQueue.main.async { self.watchForSubmission(remaining: 20) }
|
||||
}
|
||||
} else if remaining > 0 {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) {
|
||||
self.waitForHandyTranscript(remaining: remaining - 1)
|
||||
}
|
||||
} else {
|
||||
self.active = false
|
||||
self.statusItem.button?.title = "🎙屏语"
|
||||
self.showState("还没收到转写", "请确认 Handy 正在运行,然后重新开始一次。")
|
||||
}
|
||||
}
|
||||
|
||||
private func watchForSubmission(remaining: Int) {
|
||||
if !FileManager.default.fileExists(atPath: pendingFile) {
|
||||
active = false
|
||||
statusItem.button?.title = "🎙屏语"
|
||||
updateButtons()
|
||||
statusItem.button?.title = "🎙屏语·已粘贴"
|
||||
showState("已粘贴到输入框", "请检查输入框内容,确认后由你手动发送。")
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { self.showReady() }
|
||||
} else if remaining > 0 {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { self.watchForSubmission(remaining: remaining - 1) }
|
||||
} else {
|
||||
active = false
|
||||
statusItem.button?.title = "🎙屏语"
|
||||
updateButtons()
|
||||
showState("还没收到转写", "请确认 Handy 正在运行,然后重新开始一次。")
|
||||
}
|
||||
}
|
||||
|
||||
private func captureMainDisplay(completed: @escaping (String?) -> Void) {
|
||||
Task {
|
||||
do {
|
||||
let content = try await SCShareableContent.excludingDesktopWindows(false, onScreenWindowsOnly: true)
|
||||
guard let display = content.displays.first else {
|
||||
DispatchQueue.main.async { completed(nil) }
|
||||
return
|
||||
}
|
||||
let filter = SCContentFilter(display: display, excludingWindows: [])
|
||||
let configuration = SCStreamConfiguration()
|
||||
configuration.width = display.width
|
||||
configuration.height = display.height
|
||||
configuration.showsCursor = true
|
||||
let image = try await SCScreenshotManager.captureImage(contentFilter: filter, configuration: configuration)
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateFormat = "yyyyMMdd-HHmmss"
|
||||
let name = "screen-\(formatter.string(from: Date())).png"
|
||||
let directory = URL(fileURLWithPath: archiveDirectory, isDirectory: true)
|
||||
let file = directory.appendingPathComponent(name)
|
||||
try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
|
||||
let bitmap = NSBitmapImageRep(cgImage: image)
|
||||
guard let png = bitmap.representation(using: .png, properties: [:]) else {
|
||||
DispatchQueue.main.async { completed(nil) }
|
||||
return
|
||||
}
|
||||
try png.write(to: file, options: .atomic)
|
||||
DispatchQueue.main.async { completed(file.path) }
|
||||
} catch {
|
||||
let log = "\(Date()) ScreenCaptureKit error: \(error)\n"
|
||||
let logURL = URL(fileURLWithPath: archiveDirectory).appendingPathComponent("screen-app.log")
|
||||
if let data = log.data(using: .utf8) {
|
||||
if FileManager.default.fileExists(atPath: logURL.path), let handle = try? FileHandle(forWritingTo: logURL) {
|
||||
try? handle.seekToEnd(); try? handle.write(contentsOf: data); try? handle.close()
|
||||
} else {
|
||||
try? data.write(to: logURL)
|
||||
}
|
||||
}
|
||||
DispatchQueue.main.async { completed(nil) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func runPython(_ arguments: [String], completed: @escaping (Int32) -> Void) {
|
||||
let task = Process()
|
||||
task.executableURL = URL(fileURLWithPath: "/usr/bin/python3")
|
||||
task.arguments = [screenScript] + arguments
|
||||
task.terminationHandler = { process in completed(process.terminationStatus) }
|
||||
do { try task.run() } catch { completed(1) }
|
||||
}
|
||||
|
||||
private func runPythonWithInput(_ arguments: [String], input: String, completed: @escaping (Int32) -> Void) {
|
||||
let task = Process()
|
||||
task.executableURL = URL(fileURLWithPath: "/usr/bin/python3")
|
||||
task.arguments = [screenScript] + arguments
|
||||
let pipe = Pipe()
|
||||
task.standardInput = pipe
|
||||
task.terminationHandler = { process in completed(process.terminationStatus) }
|
||||
do {
|
||||
try task.run()
|
||||
pipe.fileHandleForWriting.write(input.data(using: .utf8) ?? Data())
|
||||
try? pipe.fileHandleForWriting.close()
|
||||
} catch { completed(1) }
|
||||
}
|
||||
|
||||
private func createPanel() {
|
||||
let content = NSView(frame: NSRect(x: 0, y: 0, width: 520, height: 230))
|
||||
let t = NSTextField(labelWithString: "")
|
||||
t.frame = NSRect(x: 26, y: 164, width: 468, height: 30)
|
||||
t.font = .systemFont(ofSize: 20, weight: .semibold)
|
||||
let d = NSTextField(wrappingLabelWithString: "")
|
||||
d.frame = NSRect(x: 26, y: 105, width: 468, height: 52)
|
||||
d.font = .systemFont(ofSize: 14)
|
||||
d.textColor = .secondaryLabelColor
|
||||
|
||||
let start = NSButton(title: "开始结合屏幕", target: self, action: #selector(self.start))
|
||||
start.frame = NSRect(x: 26, y: 42, width: 150, height: 38)
|
||||
start.bezelStyle = .rounded
|
||||
start.keyEquivalent = ""
|
||||
let finish = NSButton(title: "结束并粘贴", target: self, action: #selector(self.finish))
|
||||
finish.frame = NSRect(x: 184, y: 42, width: 140, height: 38)
|
||||
finish.bezelStyle = .rounded
|
||||
let permission = NSButton(title: "录屏设置", target: self, action: #selector(self.openScreenRecording))
|
||||
permission.frame = NSRect(x: 332, y: 42, width: 162, height: 38)
|
||||
permission.bezelStyle = .rounded
|
||||
|
||||
[t, d, start, finish, permission].forEach(content.addSubview)
|
||||
let p = NSPanel(
|
||||
contentRect: content.bounds,
|
||||
styleMask: [.titled, .closable, .utilityWindow, .nonactivatingPanel],
|
||||
backing: .buffered,
|
||||
defer: false
|
||||
)
|
||||
p.title = "卡若AI·屏语"
|
||||
p.contentView = content
|
||||
p.level = .floating
|
||||
p.collectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary]
|
||||
p.isReleasedWhenClosed = false
|
||||
p.hidesOnDeactivate = false
|
||||
p.becomesKeyOnlyIfNeeded = true
|
||||
panel = p
|
||||
titleLabel = t
|
||||
detailLabel = d
|
||||
startButton = start
|
||||
finishButton = finish
|
||||
updateButtons()
|
||||
}
|
||||
|
||||
private func updateButtons() {
|
||||
startButton?.isEnabled = !active
|
||||
finishButton?.isEnabled = active
|
||||
}
|
||||
|
||||
private func showState(_ title: String, _ detail: String) {
|
||||
statusItem.button?.toolTip = title + ":" + detail
|
||||
panel?.orderOut(nil)
|
||||
}
|
||||
|
||||
@objc private func openScreenRecording() {
|
||||
NSWorkspace.shared.open(URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture")!)
|
||||
}
|
||||
|
||||
@objc private func quit() { NSApp.terminate(nil) }
|
||||
}
|
||||
|
||||
let app = NSApplication.shared
|
||||
let delegate = ScreenVoiceApp()
|
||||
app.delegate = delegate
|
||||
app.run()
|
||||
23
02_卡人(水)/水溪_整理归档/语音输入/脚本/结合屏幕热键.swift
Normal file
23
02_卡人(水)/水溪_整理归档/语音输入/脚本/结合屏幕热键.swift
Normal file
@@ -0,0 +1,23 @@
|
||||
import AppKit
|
||||
import Foundation
|
||||
|
||||
let script = "/Users/karuo/Documents/个人/卡若AI/02_卡人(水)/水溪_整理归档/语音输入/脚本/结合屏幕.py"
|
||||
func trigger() {
|
||||
let task = Process()
|
||||
task.executableURL = URL(fileURLWithPath: "/usr/bin/python3")
|
||||
task.arguments = [script, "--toggle"]
|
||||
try? task.run()
|
||||
let notification = NSUserNotification()
|
||||
notification.title = "卡若AI·结合屏幕"
|
||||
notification.informativeText = "已截图并切换语音录音;再按 Command+1 结束。"
|
||||
NSUserNotificationCenter.default.deliver(notification)
|
||||
}
|
||||
|
||||
let mask: NSEvent.EventTypeMask = [.keyDown]
|
||||
NSEvent.addGlobalMonitorForEvents(matching: mask) { event in
|
||||
let commandHeld = event.modifierFlags.contains(.command)
|
||||
if event.keyCode == 18 && commandHeld { // Command + 1
|
||||
trigger()
|
||||
}
|
||||
}
|
||||
RunLoop.main.run()
|
||||
97
02_卡人(水)/水溪_整理归档/语音输入/脚本/部署结合屏幕.sh
Executable file
97
02_卡人(水)/水溪_整理归档/语音输入/脚本/部署结合屏幕.sh
Executable file
@@ -0,0 +1,97 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
ROOT="/Users/karuo/Documents/个人/卡若AI"
|
||||
BASE="$ROOT/02_卡人(水)/水溪_整理归档/语音输入"
|
||||
APP="/Applications/卡若AI·屏语.app"
|
||||
EXEC="$APP/Contents/MacOS/卡若AI·屏语"
|
||||
AGENT="$HOME/Library/LaunchAgents/com.karuo.pingyu-hotkey.plist"
|
||||
HOOK="$HOME/Library/Application Support/com.pais.handy/hooks/transcription"
|
||||
RUNTIME="$HOME/Library/Application Support/karuo-ai/pingyu"
|
||||
mkdir -p "$RUNTIME"
|
||||
cp "$BASE/脚本/结合屏幕.py" "$RUNTIME/结合屏幕.py"
|
||||
cp "$BASE/脚本/屏语截图助手.py" "$RUNTIME/屏语截图助手.py"
|
||||
cp "$BASE/脚本/handy_结合屏幕钩子.sh" "$RUNTIME/handy_结合屏幕钩子.sh"
|
||||
swiftc "$BASE/脚本/屏语图片粘贴.swift" -framework AppKit -o "$RUNTIME/屏语图片粘贴"
|
||||
chmod +x "$RUNTIME/结合屏幕.py" "$RUNTIME/屏语截图助手.py" "$RUNTIME/handy_结合屏幕钩子.sh" "$RUNTIME/屏语图片粘贴"
|
||||
ICON_SOURCE="$BASE/资源/卡若AI·屏语.png"
|
||||
OLD_APP="/Applications/卡若AI结合屏幕.app"
|
||||
OLD_AGENT="$HOME/Library/LaunchAgents/com.karuo.voice-screen-hotkey.plist"
|
||||
launchctl bootout "gui/$(id -u)" "$OLD_AGENT" 2>/dev/null || true
|
||||
rm -f "$OLD_AGENT"
|
||||
if [ -d "$OLD_APP" ] && [ ! -e "$APP" ]; then
|
||||
mv "$OLD_APP" "$APP"
|
||||
fi
|
||||
mkdir -p "$APP/Contents/MacOS" "$APP/Contents/Resources" "$HOME/Library/Logs/karuo" "$(dirname "$HOOK")"
|
||||
if [ -f "$ICON_SOURCE" ]; then
|
||||
ICONSET="$(mktemp -d)/卡若AI·屏语.iconset"
|
||||
mkdir -p "$ICONSET"
|
||||
sips -z 16 16 "$ICON_SOURCE" --out "$ICONSET/icon_16x16.png" >/dev/null
|
||||
sips -z 32 32 "$ICON_SOURCE" --out "$ICONSET/icon_16x16@2x.png" >/dev/null
|
||||
sips -z 32 32 "$ICON_SOURCE" --out "$ICONSET/icon_32x32.png" >/dev/null
|
||||
sips -z 64 64 "$ICON_SOURCE" --out "$ICONSET/icon_32x32@2x.png" >/dev/null
|
||||
sips -z 128 128 "$ICON_SOURCE" --out "$ICONSET/icon_128x128.png" >/dev/null
|
||||
sips -z 256 256 "$ICON_SOURCE" --out "$ICONSET/icon_128x128@2x.png" >/dev/null
|
||||
sips -z 256 256 "$ICON_SOURCE" --out "$ICONSET/icon_256x256.png" >/dev/null
|
||||
sips -z 512 512 "$ICON_SOURCE" --out "$ICONSET/icon_256x256@2x.png" >/dev/null
|
||||
sips -z 512 512 "$ICON_SOURCE" --out "$ICONSET/icon_512x512.png" >/dev/null
|
||||
sips -z 1024 1024 "$ICON_SOURCE" --out "$ICONSET/icon_512x512@2x.png" >/dev/null
|
||||
iconutil -c icns "$ICONSET" -o "$APP/Contents/Resources/卡若AI·屏语.icns"
|
||||
fi
|
||||
cat > "$APP/Contents/Info.plist" <<'PLIST'
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0"><dict>
|
||||
<key>CFBundleDisplayName</key><string>卡若AI·屏语</string>
|
||||
<key>CFBundleName</key><string>卡若AI·屏语</string>
|
||||
<key>CFBundleIdentifier</key><string>com.karuo.pingyu</string>
|
||||
<key>CFBundleExecutable</key><string>卡若AI·屏语</string>
|
||||
<key>CFBundleIconFile</key><string>卡若AI·屏语.icns</string>
|
||||
<key>CFBundlePackageType</key><string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key><string>1.0</string>
|
||||
<key>CFBundleVersion</key><string>1</string>
|
||||
<key>LSMinimumSystemVersion</key><string>14.0</string>
|
||||
<key>NSMicrophoneUsageDescription</key><string>卡若AI·屏语通过 Handy 使用麦克风进行语音输入。</string>
|
||||
<key>NSScreenCaptureUsageDescription</key><string>卡若AI·屏语仅在你主动启动屏语时截取当前屏幕,用于回答本次语音任务。</string>
|
||||
</dict></plist>
|
||||
PLIST
|
||||
swiftc "$BASE/脚本/结合屏幕应用.swift" -framework Cocoa -framework ApplicationServices -framework Carbon -framework ScreenCaptureKit -o "$EXEC"
|
||||
SIGN_KEYCHAIN="$HOME/Library/Keychains/karuo-pingyu-signing.keychain-db"
|
||||
SIGN_PASSWORD="karuo-pingyu-local-signing"
|
||||
SIGN_IDENTITY="3D164440DF26A1C680EC9B603EB8FD7AC8D71BD3"
|
||||
security unlock-keychain -p "$SIGN_PASSWORD" "$SIGN_KEYCHAIN"
|
||||
codesign --force --deep --options runtime --sign "$SIGN_IDENTITY" --keychain "$SIGN_KEYCHAIN" --identifier com.karuo.pingyu "$APP"
|
||||
chmod +x "$BASE/脚本/结合屏幕.py" "$BASE/脚本/handy_结合屏幕钩子.sh" "$EXEC"
|
||||
ln -sfn "$RUNTIME/handy_结合屏幕钩子.sh" "$HOOK"
|
||||
# Fn 的按键方式不变;改用外部脚本以便仅在屏语已启动时自动提交屏幕任务。
|
||||
python3 - <<PYCFG
|
||||
import json
|
||||
from pathlib import Path
|
||||
path = Path.home() / "Library/Application Support/com.pais.handy/settings_store.json"
|
||||
data = json.loads(path.read_text(encoding="utf-8"))
|
||||
settings = data.setdefault("settings", {})
|
||||
settings["paste_method"] = "ctrl_v"
|
||||
settings["external_script_path"] = None
|
||||
path.write_text(json.dumps(data, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
||||
PYCFG
|
||||
cat > "$AGENT" <<PLIST
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0"><dict>
|
||||
<key>Label</key><string>com.karuo.pingyu-hotkey</string>
|
||||
<key>ProgramArguments</key><array><string>/usr/bin/open</string><string>-g</string><string>$APP</string></array>
|
||||
<key>RunAtLoad</key><true/>
|
||||
<key>ProcessType</key><string>Interactive</string>
|
||||
<key>StandardOutPath</key><string>$HOME/Library/Logs/karuo/voice-screen-hotkey.out.log</string>
|
||||
<key>StandardErrorPath</key><string>$HOME/Library/Logs/karuo/voice-screen-hotkey.err.log</string>
|
||||
</dict></plist>
|
||||
PLIST
|
||||
launchctl bootout "gui/$(id -u)" "$AGENT" 2>/dev/null || true
|
||||
pkill -f "$EXEC" 2>/dev/null || true
|
||||
HELPER="$RUNTIME/屏语截图助手.py"
|
||||
HELPER_PID="$RUNTIME/capture-helper.pid"
|
||||
if [ -f "$HELPER_PID" ]; then kill "$(cat "$HELPER_PID")" 2>/dev/null || true; fi
|
||||
nohup /usr/bin/python3 "$HELPER" >"$RUNTIME/capture-helper.log" 2>&1 &
|
||||
launchctl bootstrap "gui/$(id -u)" "$AGENT"
|
||||
open -g "$APP"
|
||||
echo "已部署:卡若AI·屏语.app(Command+1);Fn 普通语音不变。"
|
||||
echo "首次仅需在“录屏与系统录音”中开启“卡若AI·屏语”;Command+1 不再需要输入监控。"
|
||||
BIN
02_卡人(水)/水溪_整理归档/语音输入/资源/卡若AI·屏语.png
Normal file
BIN
02_卡人(水)/水溪_整理归档/语音输入/资源/卡若AI·屏语.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 MiB |
54
03_卡木(木)/卡前_体验设计/AGENT.md
Normal file
54
03_卡木(木)/卡前_体验设计/AGENT.md
Normal file
@@ -0,0 +1,54 @@
|
||||
---
|
||||
id: kaqian
|
||||
name: 卡前
|
||||
agent_type: member
|
||||
owner: 卡木
|
||||
group: 木
|
||||
status: active
|
||||
version: "1.0"
|
||||
updated: "2026-07-26"
|
||||
---
|
||||
|
||||
# 卡前 Agent
|
||||
|
||||
## 身份与使命
|
||||
|
||||
- **角色**:卡若AI UX/UI视觉与交互设计师、前端体验交付负责人
|
||||
- **使命**:把业务需求转为可实现、可验收、可访问的页面结构、交互状态和设计系统;统筹 v0、Vercel 前端界面交付。
|
||||
- **负责人**:卡木
|
||||
- **人格**:ENFP;PDP 老虎25、猫头鹰18;DISC D力量17、C完美15
|
||||
- **工作方式**:好奇地探索用户路径,果断收敛方案,用可验证的规范保证细节。
|
||||
|
||||
## 负责范围
|
||||
|
||||
1. 信息架构、用户旅程、页面流与交互状态机。
|
||||
2. UX/UI、Design Tokens、组件规范、响应式与无障碍体验。
|
||||
3. v0 对话需求、GitHub 真源、Preview、Vercel 页面验收的体验侧总控。
|
||||
4. 前端业务界面设计评审;工程实现、埋点、构建和发布与火炬协同。
|
||||
|
||||
## Skill挂载
|
||||
|
||||
- 主责:`M14 体验设计与视觉规范输出`、`M06 前端生成`、`M11 v0前端生成与预览交付`、`M13 整站视觉采集与产品设计`。
|
||||
- 协同:`F01a 前端开发`、`F01f 私域银行统一前端模块`、`M02e 拆架契匣`。
|
||||
- 执行时从 `SKILL_REGISTRY.md` 解析真源路径;人设不替代 Skill。
|
||||
|
||||
## MCP与桥接策略
|
||||
|
||||
- 优先:`figma`、`browser`、`image`、`filesystem`、GitHub、v0/Vercel 官方 API 与本地桥接脚本。
|
||||
- v0/Vercel 的项目状态以项目内 `开发文档/8、部署/v0_vercel_binding.json` 为准;GitHub 是代码真源。
|
||||
- 密钥只从项目私有环境或账号索引引用,不写入本文件、设计稿和交付文档。
|
||||
|
||||
## Hook链
|
||||
|
||||
- `before_input` → `karuo_asr_normalize`
|
||||
- `before_route` → `karuo_skill_route`
|
||||
- `before_execute` → `karuo_scope_guard`
|
||||
- `after_execute` → `karuo_verify`
|
||||
- `after_response` → `karuo_recap`
|
||||
- `after_response` → `karuo_mongo_sync`
|
||||
|
||||
## 交付边界
|
||||
|
||||
- 不只交付理想态:必须有加载、空、错误、权限不足、超长内容和网络异常状态。
|
||||
- 不只交付审美描述:必须交付页面结构、状态流、Token、组件规则、a11y 与前端映射建议。
|
||||
- 视觉稿与代码不一致时,先形成差异清单;由火炬处理工程实现,卡前回归验收。
|
||||
75
03_卡木(木)/卡前_体验设计/体验设计与视觉规范输出/SKILL.md
Normal file
75
03_卡木(木)/卡前_体验设计/体验设计与视觉规范输出/SKILL.md
Normal file
@@ -0,0 +1,75 @@
|
||||
---
|
||||
name: 体验设计与视觉规范输出
|
||||
description: 卡前负责 UX/UI、用户旅程、交互状态机、Design Tokens、组件规格、无障碍和前端体验验收。触发词:UX、UI、体验设计、视觉设计、页面设计、用户旅程、交互状态、设计系统、Design System、设计令牌、无障碍、a11y、前端界面、v0界面、Vercel预览。
|
||||
triggers: UX、UI、体验设计、视觉设计、页面设计、用户旅程、交互状态、设计系统、Design System、设计令牌、无障碍、a11y、前端界面、v0界面、Vercel预览
|
||||
owner: 卡前
|
||||
group: 木
|
||||
version: "1.0"
|
||||
updated: "2026-07-26"
|
||||
---
|
||||
|
||||
# 体验设计与视觉规范输出(M14)
|
||||
|
||||
## 定位
|
||||
|
||||
卡前将业务目标转成可被前端直接实现和验收的体验规格。代码真源仍在 GitHub;v0/Vercel 交付沿用 M11;工程实现和埋点沿用 F01a/F01f。
|
||||
|
||||
## 工作流
|
||||
|
||||
1. **用户旅程**:读取 PRD、页面现状和数据,输出角色、目标、触点、痛点、成功条件。
|
||||
2. **信息架构**:定义页面、模块、导航和关键任务路径;用 Mermaid 描述主流程。
|
||||
3. **交互状态**:为每个关键模块补齐 `loading / empty / error / disabled / overflow / offline / success` 状态及恢复动作。
|
||||
4. **设计系统**:输出颜色、字体、间距、圆角、阴影、层级、动效和组件状态,映射 CSS 变量与 Tailwind Token。
|
||||
5. **无障碍**:标明对比度、焦点顺序、键盘操作、语义标签、表单错误提示和触控尺寸。
|
||||
6. **前端交付**:向同一 v0 Chat 发送结构化需求;由 GitHub 分支写回、Preview 与 Vercel 验收。卡前负责视觉回归,火炬负责工程回归。
|
||||
|
||||
## 必交付格式
|
||||
|
||||
每次页面/功能设计至少包含:
|
||||
|
||||
- 用户旅程与页面结构;
|
||||
- Mermaid 状态流;
|
||||
- Design Tokens(Hex/RGB、字号、间距、阴影、圆角);
|
||||
- 组件清单和状态表;
|
||||
- a11y 清单;
|
||||
- v0 提示词或前端实现约束;
|
||||
- 验收清单与截图对照点。
|
||||
|
||||
## 状态流模板
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Loading
|
||||
Loading --> Ready: 数据成功
|
||||
Loading --> Empty: 无数据
|
||||
Loading --> Error: 请求失败
|
||||
Error --> Loading: 重试
|
||||
Empty --> Ready: 创建或导入
|
||||
Ready --> [*]
|
||||
```
|
||||
|
||||
## Design Tokens 最小集
|
||||
|
||||
```css
|
||||
:root {
|
||||
--color-primary: #2563EB;
|
||||
--color-primary-hover: #1D4ED8;
|
||||
--color-surface: #FFFFFF;
|
||||
--color-text: #0F172A;
|
||||
--color-muted: #64748B;
|
||||
--color-danger: #DC2626;
|
||||
--space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px;
|
||||
--radius-sm: 8px; --radius-md: 12px; --radius-lg: 16px;
|
||||
--shadow-card: 0 8px 24px rgb(15 23 42 / 8%);
|
||||
}
|
||||
```
|
||||
|
||||
- 正文与背景对比度目标不低于 4.5:1;大文字不低于 3:1。
|
||||
- 键盘焦点可见;完整操作顺序与视觉顺序一致;可点击目标最小 44×44px。
|
||||
- Skeleton 优先于无限 Spinner;错误状态必须给出重试或返回路径。
|
||||
|
||||
## 协同与验收
|
||||
|
||||
- 需要生成或部署界面时叠加 M11;需要工程代码时叠加 F01a;需要接口契约时叠加 M02e。
|
||||
- 禁止以“页面好看”作为验收结论;至少验证主路径、所有状态、移动端、键盘导航、对比度和 Preview。
|
||||
- 将规格与验收写入项目 `开发文档/`,但本 Skill、人设、路由与能力归属只维护在卡若AI主仓。
|
||||
4
03_卡木(木)/卡前_体验设计/体验设计与视觉规范输出/agents/openai.yaml
Normal file
4
03_卡木(木)/卡前_体验设计/体验设计与视觉规范输出/agents/openai.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
interface:
|
||||
display_name: "卡前·体验设计与视觉规范"
|
||||
short_description: "UX/UI、状态机、设计系统、无障碍与前端体验验收"
|
||||
default_prompt: "以卡前身份梳理用户旅程、交互状态和设计系统,并输出可直接交付给前端与 v0 的体验规格。"
|
||||
@@ -14,14 +14,14 @@ updated: "2026-07-23"
|
||||
## 身份与使命
|
||||
|
||||
- **角色**:木果核心执行Agent
|
||||
- **使命**:模板、PPT、前端形态、整站视觉采集、产品设计资产和品牌叙事
|
||||
- **使命**:模板、PPT、整站视觉采集、产品设计资产和品牌叙事(前端体验交付由卡前主责)
|
||||
- **负责人**:卡木
|
||||
- **人格**:ESTP;老虎+I
|
||||
- **工作模式**:`塑形`、`木芽表达模式`
|
||||
|
||||
## Skill挂载
|
||||
|
||||
`M03`、`M07`、`M08`、`M09`、`M13`
|
||||
`M03`、`M07`、`M08`、`M09`;M13由卡前主责、木果协同
|
||||
|
||||
执行时必须从`SKILL_REGISTRY.md`解析真实路径;人设本身不替代Skill。
|
||||
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
name: v0前端生成与预览交付
|
||||
description: 负责部署到 Vercel 的对接(主要是前端界面);v0 优化完成后同步到 GitHub、Vercel、本地协同,确保全部确定;任务完成反馈写入项目并反馈卡若。项目/对话名中文。
|
||||
triggers: Vercel部署/v0部署/部署到v0/上帝之眼部署/Vercel v0/威宁 v0/部署流水线
|
||||
owner: 木果
|
||||
owner: 卡前
|
||||
group: 木
|
||||
version: "3.0"
|
||||
updated: "2026-07-24"
|
||||
---
|
||||
|
||||
> **体验负责人**:卡前负责用户旅程、交互状态、视觉规范、无障碍与最终体验验收;木果保留项目模板、内容与设计资产协同。
|
||||
|
||||
> **执行规则**:见 `运营中枢/技能路由/SKILL_RULES.md`。执行前必须:思考→拆解→读取本 Skill 与 references→按步执行→校验 v0 项目正确→**执行完成后将结果反馈给卡若**。
|
||||
|
||||
## 一、技能概述
|
||||
@@ -198,6 +200,15 @@ node scripts/sync-to-v0.js
|
||||
5. 回传 demo、截图、版本 ID 与变更摘要。
|
||||
6. v0 生成代码写入 Git 分支后,GitHub 合并/推送触发 Vercel;再回读部署、生产 URL、HTTP 状态与页面标题。
|
||||
|
||||
### 8.4-A 分支问题一键修复(新增)
|
||||
|
||||
- 当出现 `Create a Branch`、`continue` 被拦截等提示时,不点页面:
|
||||
1. `POST /v1/chats/{chatId}/fork` 创建分支 chat(返回新 chatId)。
|
||||
2. 将新 chatId 写回 `sdk/config/v0_vercel_binding.json` 的 `v0.chatId` 与 `v0.chatUrl`,避免反复操作旧会话。
|
||||
3. 后续都使用新 chatId 继续 `send/status`,旧 chat 只留归档。
|
||||
- 规则:一个主问题不再创建新分支,若同一目标 1h 内重复 fork 先复用同一 chat。
|
||||
- 示例命令:`node sdk/scripts/v0_control.mjs fork` / `node sdk/scripts/v0_control.mjs switch <chatId>`。
|
||||
|
||||
### 8.5 完成判定
|
||||
|
||||
以下证据全部存在才写 100%:
|
||||
|
||||
@@ -3,11 +3,13 @@ name: 前端生成
|
||||
description: 现代UI代码生成,默认遵循卡若标准(神射手/毛狐狸):苹果毛玻璃、渐变背景、统一色板与组件。触发词:生成UI、前端代码、设计页面、生成组件、神射手、毛狐狸、毛玻璃、卡若标准。
|
||||
group: 木
|
||||
triggers: 前端生成、UI生成、页面生成、神射手风格、毛狐狸风格、毛玻璃
|
||||
owner: 木果
|
||||
owner: 卡前
|
||||
version: "3.0"
|
||||
updated: "2026-07-22"
|
||||
---
|
||||
|
||||
> **体验负责人**:卡前负责用户旅程、交互状态、视觉规范、无障碍与最终体验验收;木果保留项目模板、内容与设计资产协同。
|
||||
|
||||
# 前端生成
|
||||
|
||||
> **强制入口**:所有 Web 前端生成先读取同级 `v0前端生成与预览交付/SKILL.md` 与 `参考资料/v0通用前端基线.md`。默认产物必须能被 v0 当前环境直接导入、运行、预览并通过 Git/PR 写回。
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
name: 整站视觉采集与产品设计
|
||||
description: 使用SiteOne Crawler与Playwright采集整站页面、全页截图、交互状态、静态资源、离线副本和Markdown,并生成产品设计图册、页面清单、设计分析与前端还原文档。适用于参考网站提取、竞品产品设计、改版留档、新旧站视觉对比和前端验收。
|
||||
metadata:
|
||||
owner: 木果
|
||||
owner: 卡前
|
||||
group: 木
|
||||
version: "1.0"
|
||||
updated: "2026-07-23"
|
||||
---
|
||||
|
||||
> **体验负责人**:卡前负责用户旅程、交互状态、视觉规范、无障碍与最终体验验收;木果保留项目模板、内容与设计资产协同。
|
||||
|
||||
# 整站视觉采集与产品设计
|
||||
|
||||
## 目标
|
||||
|
||||
@@ -12,6 +12,8 @@ updated: "2026-07-22"
|
||||
|
||||
> 主责:**苹果毛玻璃风格**与卡若标准前端(神射手/毛狐狸)。全栈开发做官网/全站时,前端部分统一走本 Skill。
|
||||
|
||||
> **协同边界**:卡前负责体验设计、状态机、Design Tokens、无障碍和视觉验收;火炬负责工程实现、埋点、构建和发布验收。
|
||||
|
||||
> **工程基线前置**:Web 项目必须先执行木果 `v0前端生成与预览交付` 的 `参考资料/v0通用前端基线.md`;本 Skill 负责视觉、业务实现、埋点和质量,不另建一套与 v0 不兼容的脚手架。
|
||||
|
||||
---
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 卡若AI Agent注册表
|
||||
|
||||
> 固定人设真源:1名大总管(BOOTSTRAP)+ 5名负责人Agent + 18名核心执行Agent。Skill只作为能力挂载。
|
||||
> 固定人设真源:1名大总管(BOOTSTRAP)+ 5名负责人Agent + 19名核心执行Agent。Skill只作为能力挂载。
|
||||
|
||||
| ID | Agent | 类型 | 五行 | 负责人 | 人设真源 | 工作模式 | Skill挂载 | MCP策略 |
|
||||
|:---|:---|:---|:---|:---|:---|:---|:---|:---|
|
||||
@@ -18,6 +18,7 @@
|
||||
| `muye` | 木叶 | member | 木 | 卡木 | `03_卡木(木)/木叶_视频内容/AGENT.md` | 内容生产、分发 | M01、M01h | inherit |
|
||||
| `mugen` | 木根 | member | 木 | 卡木 | `03_卡木(木)/木根_逆向分析/AGENT.md` | 深析、接口识别 | M02、M02a | inherit |
|
||||
| `muguo` | 木果 | member | 木 | 卡木 | `03_卡木(木)/木果_项目模板/AGENT.md` | 塑形、木芽表达模式 | M03、M07、M08、M09 | inherit |
|
||||
| `kaqian` | 卡前 | member | 木 | 卡木 | `03_卡木(木)/卡前_体验设计/AGENT.md` | 用户旅程、交互状态、设计系统、前端体验交付 | M14、M06、M11、M13;协同F01a、F01f、M02e | inherit |
|
||||
| `mushi` | 木识 | member | 木 | 卡木 | `03_卡木(木)/木识_软件识形/AGENT.md` | 识窍、CLI | M10 | inherit |
|
||||
| `huoju` | 火炬 | member | 火 | 卡火 | `04_卡火(火)/火炬_全栈消息/AGENT.md` | 炬彻架构模式、络照集成模式 | F01、F01a、F01b、F28、F29 | inherit |
|
||||
| `huochui` | 火锤 | member | 火 | 卡火 | `04_卡火(火)/火锤_代码修复/AGENT.md` | 锤砺修复模式 | F05 | inherit |
|
||||
|
||||
@@ -17,10 +17,10 @@
|
||||
- `AGENT_REGISTRY.md`:人设可读索引。
|
||||
- `运营中枢/智能体路由/agent_registry.json`:网站、Codex与运行时共用的结构化Agent真源。
|
||||
|
||||
## 二、团队(5 负责人 Agent → 18 核心成员 Agent → Skill/MCP/Hook → 智能体实例)
|
||||
## 二、团队(5 负责人 Agent → 19 核心成员 Agent → Skill/MCP/Hook → 智能体实例)
|
||||
|
||||
```
|
||||
卡若AI → 卡资(金)→金仓·金盾 | 卡人(水)→水溪·水泉·水桥·水岸 | 卡木(木)→木叶·木根·木果·木识 | 卡火(火)→火炬·火锤·火眼·火种 | 卡土(土)→土基·土砖·土渠·土簿
|
||||
卡若AI → 卡资(金)→金仓·金盾 | 卡人(水)→水溪·水泉·水桥·水岸 | 卡木(木)→木叶·木根·木果·卡前·木识 | 卡火(火)→火炬·火锤·火眼·火种 | 卡土(土)→土基·土砖·土渠·土簿
|
||||
```
|
||||
|
||||
分配:用户明确@负责人或成员 → Agent注册表精确命中 → Skill注册表匹配能力 → 加载该Agent的MCP与Hook → 执行并验收。固定人员为5名负责人Agent与18名核心成员Agent;旧五席名称仅作为工作模式与兼容别名。**新技能 / 改名**:须符合 **卡若记忆宫殿命名体系**,见 `运营中枢/工作台/01_技能控制台.md` **〇、卡若记忆宫殿命名体系**。
|
||||
@@ -55,7 +55,8 @@
|
||||
| **SiteOne/整站截图/网站提取/参考网站/产品设计/产品图/设计图册/按钮截图/视觉回归** | M13 `03_卡木(木)/木果_项目模板/整站视觉采集与产品设计/SKILL.md` |
|
||||
| **持续执行/永久执行/接着做/直到完成** | `04_卡火(火)/火炬_全栈消息/持续执行闭环/SKILL.md` |
|
||||
| **写书/写一本书/聊天记录写书/畅销书/正式出版稿/正式版PDF/出版社送审包/直到出版** | F13b `04_卡火(火)/火种_知识模型/卡若写书出版/SKILL.md`(任何书唯一总入口;逐章写作叠F13a) |
|
||||
| **闽南话/语音输入/ASR 误听/回廊洗字** | 机制 `运营中枢/参考资料/闽南话语音_ASR纠错机制.md`;词库 `运营中枢/参考资料/卡若闽南口音_ASR纠错库.json`;W03b `02_卡人(水)/水溪_整理归档/语音转写纠错/SKILL.md` |
|
||||
| **语音输入/Handy/Fn语音/Command+1/结合屏幕/SenseVoice** | W03h `02_卡人(水)/水溪_整理归档/语音输入/SKILL.md`;设备安装资料由该 Skill 统一引用 |
|
||||
| **闽南话/ASR 误听/回廊洗字** | 机制 `运营中枢/参考资料/闽南话语音_ASR纠错机制.md`;词库 `运营中枢/参考资料/卡若闽南口音_ASR纠错库.json`;W03b `02_卡人(水)/水溪_整理归档/语音转写纠错/SKILL.md` |
|
||||
| **高风险提示词/平和版/翻译提示词** | W05e `02_卡人(水)/水泉_规划拆解/执行提示词改写/SKILL.md`(洗字之后、公示之前) |
|
||||
| Soul运营报表 | `02_卡人(水)/水桥_平台对接/飞书管理/运营报表_SKILL.md` |
|
||||
| **Soul派对技能流/派对Stream/创业派对Stream** | `02_卡人(水)/水岸_项目管理/卡若创业派对/Soul派对技能流_掌管人与Stream规约.md` |
|
||||
|
||||
@@ -90,7 +90,6 @@
|
||||
| G23 | **宿主挂载执行**(Shell 宿主语义) | 金仓 | **宿主挂载、宿主执行、云上 bash、万推控制台、挂载出错、karo_exec、自动 bash Mac 路径、brew 云端、Docker 宿主 shell、宿主程序** | `01_卡资(金)/金仓_存储备份/宿主挂载执行_SHELL/SKILL.md` | 网站对话与云端 shell ≠ Mac;工作区挂载 + docker-mac-host-shell + 宿主预检三位一体 |
|
||||
| G24 | **深蓝云自动化**(SLCloud · SHENLANYUN) | 金盾 | **深蓝云、深澜云、SHENLANYUN、slcloud、slcloud.org、slcloud-automation、订阅链接、体验套餐、深澜文档、token=、client/subscribe** | `01_卡资(金)/金盾_数据安全/深蓝云_SLCloud自动化/SKILL.md` | 深澜云平台注册/购套餐/取订阅链接;**支付与扫码必须用户接管**;真源自 `slcloud-automation.skill` |
|
||||
| G25 | **UU远程**(网易 · 遥台) | 金仓 | **UU远程、远程安装、远程装Codex、远程装软件、UU终端、远程插件、OFFICE-INNER、DESKTOP-434GEGI** | `01_卡资(金)/金仓_存储备份/UU远程/SKILL.md` | UURemote 本机安装 + **终端零手操**装任意软件/插件;`uu_remote_install_software.sh` / `uu_remote_install_codex.sh` |
|
||||
| G25b | **Handy中文语音** | 金仓 | **Handy、卡若AI语音、语音唤醒、呼叫卡若AI、⌘1、Ctrl+1、SenseVoice、按住说话、Handy中文** | `01_卡资(金)/金仓_存储备份/Handy中文语音/SKILL.md` | 双平台 Handy 中文+**语音唤 Agent**(Hub 13458);zip 在 `4、火_工具开发/工具/Handy中文语音/` |
|
||||
| G27 | **Codex账号备份恢复** | 金仓·库政 | **Codex备份、Codex恢复、账号切换恢复、Codex迁移、Codex材料恢复、Codex记录恢复** | `01_卡资(金)/金仓_存储备份/CodexAccountBackupRestore/SKILL.md` | Codex配置、Skill、记忆、会话索引和状态库备份恢复 |
|
||||
| G28 | **Codex对话状态修复** | 金仓·算巡 | **Codex侧栏修复、Codex对话恢复、Codex项目分类、Codex置顶、rollout缺失、thread-store internal error、session_index、state_5.sqlite、公司NAS Mongo备份、跨Codex恢复** | `01_卡资(金)/金仓_存储备份/Codex对话状态修复/SKILL.md` | 修复Codex侧栏与状态库;按项目/工作树保存完整事件链到公司NAS Mongo,并生成跨实例恢复包 |
|
||||
| G29 | **域名调研注册** | 金仓·网卫 | **域名调研、域名注册、域名抢注、老域名、ICP备案、历史备案、WHOIS、RDAP、域名风险** | `01_卡资(金)/金仓_存储备份/域名调研注册/SKILL.md` | 域名候选、历史流量、备案、风险核验和注册执行 |
|
||||
@@ -104,7 +103,8 @@
|
||||
| W02 | 文档清洗 | 水溪 | PDF转Markdown | `02_卡人(水)/水溪_整理归档/文档清洗/SKILL.md` | PDF/Word 转结构化 Markdown |
|
||||
| W03 | 对话归档 | 水溪 | 归档今日对话 | `02_卡人(水)/水溪_整理归档/对话归档/SKILL.md` | AI 对话记录收集与归类 |
|
||||
| W03a | **项目调研** | 水溪 | **项目调研、平台分析、A群、A群聊天记录、聊天记录清理、对话分类、对话分类号、按项目归档、调研归档、APP资料、其他APP、各APP** | `02_卡人(水)/水溪_整理归档/项目调研/SKILL.md` | 平台分析/项目调研/各APP资料/群聊/对话分类统一归档到 开发/7.项目调研,按项目分子目录 |
|
||||
| W03b | **回廊洗字**(卡若记忆宫殿命名体系 · 原名语音转写纠错) | 水溪 | **回廊洗字、卡若记忆宫殿命名体系、记忆宫殿、记忆空间、语音转写纠错、语音输入、闽南话、闽南口音、听写、ASR、转写纠错、纠错库、误听、口述、嘴瓢、卡罗拉、卡罗伊、网页CLI、终端浏览器、browsh、命令行看网页** | `02_卡人(水)/水溪_整理归档/语音转写纠错/SKILL.md` | `卡若记忆宫殿/水殿/水溪厢/回廊洗字`:口述滤真;ASR 纠错 JSON;`soul_enhance` 合并;**「星数」误听「西游记」**时 Browsh 备忘 |
|
||||
| W03b | **回廊洗字**(卡若记忆宫殿命名体系 · 原名语音转写纠错) | 水溪 | **回廊洗字、卡若记忆宫殿命名体系、记忆宫殿、记忆空间、语音转写纠错、闽南话、闽南口音、听写、ASR、转写纠错、纠错库、误听、口述、嘴瓢、卡罗拉、卡罗伊、网页CLI、终端浏览器、browsh、命令行看网页** | `02_卡人(水)/水溪_整理归档/语音转写纠错/SKILL.md` | `卡若记忆宫殿/水殿/水溪厢/回廊洗字`:口述滤真;ASR 纠错 JSON;`soul_enhance` 合并;**「星数」误听「西游记」**时 Browsh 备忘 |
|
||||
| W03h | **语音输入** | 水溪 | **语音输入、Handy、卡若AI语音、Fn语音、Command+1、结合屏幕、免按住说话、SenseVoice、中文听写、语音唤醒、语音配置** | `02_卡人(水)/水溪_整理归档/语音输入/SKILL.md` | 卡水统一入口:Handy 日常语音输入、配置基线、验收与排障;纠错委托 W03b 回廊洗字 |
|
||||
| W03c | **AI平台代理申请** | 水溪 | **AI代理、API代理、分销申请、代理邮件、API平台代理、全球AI代理、AI平台代理申请、伙伴计划申请、send_ai_partner、Reseller、partner申请、代理权、affiliate申请** | `02_卡人(水)/水溪_整理归档/AI平台代理申请/SKILL.md` | 向各大 AI 平台申请代理/分销:调研+实力图+SMTP 批量邮件+表单清单;Mongo 经验;项目真源 `开发/7.项目调研/AI平台代理分销/`;原名伙伴投函 |
|
||||
| W03d | **工作日志** | 水溪 | **工作日志、写工作日志、运营日志、本地日志、4、工作日志、今日工作日志、写今日日志、星图阁** | `02_卡人(水)/水溪_整理归档/工作日志/SKILL.md` | 本地 `4、工作日志` Obsidian;Agent 三块复盘+星图阁可点链+`--open` |
|
||||
| W03e | **公开源人脉调研** | 水溪 | **公开源人脉调研、企查查调研、黑曜石星图、人脉关系链、工商穿透、法人调研、股权关联、企查猫** | `02_卡人(水)/水溪_整理归档/公开源人脉调研/SKILL.md` | 仅线上公开源;单文件合并交付 `卡若Ai的文件夹/调研/`;禁止性格推断与本地聊天记录 |
|
||||
@@ -177,19 +177,20 @@
|
||||
| M07 | PPT制作 | 木果 | **PPT、做PPT、制作PPT、演示文稿、汇报PPT** | `03_卡木(木)/木果_项目模板/PPT制作/SKILL.md` | python-pptx 创建/编辑 .pptx,输出到报告目录 |
|
||||
| M08 | Next AI Draw | 木果 | **next ai draw、AI画图、画图表、架构图、流程图** | `03_卡木(木)/木果_项目模板/Next AI Draw/SKILL.md` | AI 生成 draw.io 风格图、Mermaid 图表,与 PPT 联动 |
|
||||
| M09 | 卡若个人介绍 | 木果 | **卡若介绍、个人介绍、卡若人设、我是谁** | `03_卡木(木)/木果_项目模板/卡若个人介绍/SKILL.md` | 生成卡若个人介绍(PPT/短文/一页纸) |
|
||||
| M13 | **整站视觉采集与产品设计** | 木果 | **SiteOne、整站截图、全站截图、网站提取、参考网站、产品设计、产品图、设计图册、按钮截图、交互状态、离线网站、网站转Markdown、视觉回归** | `03_卡木(木)/木果_项目模板/整站视觉采集与产品设计/SKILL.md` | SiteOne深爬+全页截图+离线副本+Markdown;Playwright补交互状态;生成产品设计说明、图册、页面清单和前端验收文档 |
|
||||
| M13 | **整站视觉采集与产品设计** | 卡前 | **SiteOne、整站截图、全站截图、网站提取、参考网站、产品设计、产品图、设计图册、按钮截图、交互状态、离线网站、网站转Markdown、视觉回归** | `03_卡木(木)/木果_项目模板/整站视觉采集与产品设计/SKILL.md` | SiteOne深爬+全页截图+离线副本+Markdown;Playwright补交互状态;生成产品设计说明、图册、页面清单和前端验收文档 |
|
||||
| M14 | **体验设计与视觉规范输出** | 卡前 | **UX、UI、体验设计、视觉设计、页面设计、用户旅程、交互状态、设计系统、Design System、设计令牌、无障碍、a11y、前端界面、v0界面、Vercel预览** | `03_卡木(木)/卡前_体验设计/体验设计与视觉规范输出/SKILL.md` | 用户旅程→状态机→Design Tokens→a11y→v0/GitHub/Vercel体验验收;工程实现协同F01a、F01f、M02e |
|
||||
|
||||
## 火组 · 卡火(技术研发优化)
|
||||
|
||||
| # | 技能 | 成员 | 触发词 | SKILL 路径 | 一句话 |
|
||||
|:--|:---|:---|:---|:---|:---|
|
||||
| F01 | 全栈开发 | 火炬 | 知己、RAG、分销、**卡若AI官网、官网开发、全站开发、开发文档、1~10**、**埋点、点击统计、用户行为、点击锚点、trackClick** | `04_卡火(火)/火炬_全栈消息/全栈开发/SKILL.md` | 全栈项目 + 官网/全站与开发文档 1~10;**§1.10 埋点全站强制**;获客/深链路 **§1.11 仅索引**,详规 **F23~F27** |
|
||||
| F01a | 前端开发 | 火炬 | **前端开发、毛玻璃、神射手风格、毛狐狸风格、前端标准、苹果毛玻璃**、**埋点、点击锚点、trackClick、用户行为** | `04_卡火(火)/火炬_全栈消息/前端开发/SKILL.md` | 毛玻璃 + 前端标准;**§五 用户行为与点击锚点**;详规见全栈 §1.10 |
|
||||
| F01a | 前端开发 | 卡前/火炬 | **前端开发、毛玻璃、神射手风格、毛狐狸风格、前端标准、苹果毛玻璃**、**埋点、点击锚点、trackClick、用户行为** | `04_卡火(火)/火炬_全栈消息/前端开发/SKILL.md` | 毛玻璃 + 前端标准;**§五 用户行为与点击锚点**;详规见全栈 §1.10 |
|
||||
| F01b | 全栈测试 | 火炬 | **全栈测试、功能测试、回归测试、深度测试、E2E测试、API测试、发布测试、测试验收** | `04_卡火(火)/火炬_全栈消息/全栈开发/全栈测试/SKILL.md` | 功能开发后系统化验收:前端/后端/数据库/脚本/发布引擎五维测试;**每完成一个功能必须调用** |
|
||||
| F01c | **项目开发占卜术**(间名 **演门测机**) | 火炬 | **项目开发占卜术、开发占卜、Q门3.0、奇门项目盘、八门复盘、起盘、盘势、门迫** | `04_卡火(火)/火炬_全栈消息/项目开发占卜术/SKILL.md` | 奇门 Q门 3.0 八门健康度扫描;**仅用户点名起盘**时附在复盘 v6.0 五块**之后**,**不**写入标准 🎯 |
|
||||
| F01d | **卡若复盘格式** | 火炬 | **复盘格式、卡若复盘、达成率怎么写、复盘 v6、视频号分发复盘、健康研究复盘** | `04_卡火(火)/火炬_全栈消息/卡若复盘格式/SKILL.md` | v6.1:五段图标标题去掉一二三四五;🎯目标、✅结果、📊达成率分开,🧭过程、🔍反思、📌总结、🚀下一步执行 |
|
||||
| F01e | **开发五角色与飞书里程碑** | 炬彻 | **开发五角色、五方演岗、飞书里程碑、完整功能推送、项目里程碑卡片** | `04_卡火(火)/火炬_全栈消息/开发五角色与飞书里程碑/SKILL.md` | 开发角色协作、功能验收和飞书里程碑通报 |
|
||||
| F01f | **私域银行统一前端模块** | 炬彻 | **Glass UI Kit、统一前端模块、ui-kit、私域银行前端、kir设计系统** | `04_卡火(火)/火炬_全栈消息/前端开发/私域银行统一前端模块/SKILL.md` | 可复用Glass UI组件、设计令牌和业务预设 |
|
||||
| F01f | **私域银行统一前端模块** | 卡前/火炬 | **Glass UI Kit、统一前端模块、ui-kit、私域银行前端、kir设计系统** | `04_卡火(火)/火炬_全栈消息/前端开发/私域银行统一前端模块/SKILL.md` | 可复用Glass UI组件、设计令牌和业务预设 |
|
||||
| F01g | **无界面命令行项目执行闭环** | 火炬 | **无界面命令行、命令行项目执行、API自动操作、Agent命令行、统一回执、证据回收、类似项目复用、项目适配表** | `04_卡火(火)/火炬_全栈消息/无界面命令行项目执行闭环/SKILL.md` | 将CLI/API/SDK/Agent/Hook统一为探测、执行、反馈、重试、回读、测试和Skill沉淀闭环 |
|
||||
| G26 | **持续执行闭环** | 火炬 | **持续执行、永久执行、持久执行、接着做、继续开发、直到完成、边做边标、任务闭环、不要停、自主推进、每轮读需求、开发至验收** | `04_卡火(火)/火炬_全栈消息/持续执行闭环/SKILL.md` | 通用做到验收+边打勾+全量测试;与 **G22b** 叠加见 `Cursor持久对话/references/不间断开发_G22b叠加G26.md` |
|
||||
| F02 | 消息中枢 | 火炬 | WhatsApp、Telegram | `04_卡火(火)/火炬_全栈消息/消息中枢/SKILL.md` | 多平台消息聚合 |
|
||||
@@ -264,11 +265,11 @@
|
||||
| F13 | 卡若日记写作 | 火种 | 日记、写日记 | `04_卡火(火)/火种_知识模型/卡若日记写作/SKILL.md` | 每日日记;**全程叠 F13a 卡若真人写作** |
|
||||
| F13a | **卡若真人写作** | 火种 | **写文章、写日记、写干货、写分享、Soul写文章、卡路、派对文章、去AI、humanize、humanizer、真人写作、不像AI** | `04_卡火(火)/火种_知识模型/卡若真人写作/SKILL.md` | **写作统一中枢**:写前+写中+写后;**每次必扫** `参考资料/卡若语气_有力营销常用语.md` + `经验沉淀_中文全集.md`;Soul 结构仍叠子 Skill |
|
||||
| F13b | **卡若写书出版** | 火种 | **写书、写一本书、写任何一本书、聊天记录写书、MBTI写书、畅销书、出版一本书、写书直到出版、正式出版稿、正式版PDF、出版社送审包、IMAGE2配图、全书审校、出版验收、ISBN、CIP** | `04_卡火(火)/火种_知识模型/卡若写书出版/SKILL.md` | **任何书的唯一总入口**:真实素材→目录→逐章写作→视觉→正式MD/DOCX/PDF→全页QA→出版社送审;逐章声线叠F13a |
|
||||
| M11 | v0前端生成与预览交付 | 木果 | Vercel、v0、新建前端、前端预览、Next.js、GitHub绑定、Duplicate and Upgrade、Not found、向v0发送消息、Word转前端、Worktree协同 | `03_卡木(木)/木果_项目模板/v0前端生成与预览交付/SKILL.md` | v0通用前端基线→GitHub真源→Sandbox/VM→Preview→分支/PR→Vercel→持续对话闭环 |
|
||||
| M11 | v0前端生成与预览交付 | 卡前 | Vercel、v0、新建前端、前端预览、Next.js、GitHub绑定、Duplicate and Upgrade、Not found、向v0发送消息、Word转前端、Worktree协同 | `03_卡木(木)/木果_项目模板/v0前端生成与预览交付/SKILL.md` | v0通用前端基线→GitHub真源→Sandbox/VM→Preview→分支/PR→Vercel→持续对话闭环 |
|
||||
| G21 | 端口登记 | 工作台 | **端口登记、端口注册、部署端口、查端口、避免端口冲突、端口冲突、本机端口** | `运营中枢/工作台/端口登记/SKILL.md` | 本机 Docker/服务端口统一登记,部署前必查防重复;**放在卡若AI 底下,不放在 Cursor** |
|
||||
| 卡罗帮 | **专注静默** | 工作台 | **专注、番茄钟、静默专注、开始专注、WebPomodoro、专注轻探、不打开界面计时** | `运营中枢/工作台/卡罗帮/专注静默_SKILL.md` | 不打开 WebPomodoro 界面,用 pomodoro_silent.py 静默计时 |
|
||||
| G20 | 个人档案生成器 | 金盾 | 个人档案 | `01_卡资(金)/金盾_数据安全/个人档案生成器/SKILL.md` | 安全视角的个人档案 |
|
||||
| M06 | 前端生成 | 木果 | 前端生成、UI | `03_卡木(木)/木果_项目模板/前端生成/SKILL.md` | 快速生成前端页面 |
|
||||
| M06 | 前端生成 | 卡前 | 前端生成、UI | `03_卡木(木)/木果_项目模板/前端生成/SKILL.md` | 快速生成前端页面 |
|
||||
|
||||
---
|
||||
|
||||
|
||||
64
开发文档/东京服务器系统级VPN部署_20260726.md
Normal file
64
开发文档/东京服务器系统级VPN部署_20260726.md
Normal file
@@ -0,0 +1,64 @@
|
||||
# 东京服务器系统级 VPN(IKEv2 + L2TP)部署 2026-07-26
|
||||
|
||||
## 🎯 目标
|
||||
- 43.165.178.230(腾讯云东京 Lighthouse)已部署 strongSwan IKEv2 + xl2tpd L2TP/IPSec
|
||||
- macOS「系统设置 → 网络 → VPN」原生支持,无需任何中间软件
|
||||
- 当前进度: **服务器端100%完成**,本地等用户换网络(手机热点/公司网)即可连
|
||||
|
||||
## 🔍 根因诊断(TAT)
|
||||
| 项 | 状态 |
|
||||
|---|---|
|
||||
| 服务器系统 | Ubuntu 24.04.4 LTS |
|
||||
| sshd | 监听 0.0.0.0:22022 ✓ |
|
||||
| UFW | active,SSH 22022 已放行本机公网IP 112.48.91.26 |
|
||||
| 强Swan | strongSwan U5.9.13/K6.8.0-124-generic |
|
||||
| **本机到服务器网络** | ❌ **所有端口超时**(TCP/80、UDP/500、UDP/1701 全断) |
|
||||
| 真源 | 锐捷路由器出口(119.233.229.4)到43.165.178.230路径上有节点断 |
|
||||
|
||||
## ✅ 服务器端已部署
|
||||
- **strongSwan 5.9.13** 已装并启动(charon 进程)
|
||||
- **xl2tpd 1.3.18** 已装并启动(active running)
|
||||
- **IP 转发** 已开启
|
||||
- **5条防火墙规则** 已添加(500/4500/1701/50/51 UDP)
|
||||
- **IKEv2证书** strongSwan自带(无客户端证书要求,纯用户名密码)
|
||||
- **账号** karuo / KaruoVPN@2026(EAP-MSCHAPv2)
|
||||
- **L2TP共享密钥** KaruoL2TP@2026
|
||||
|
||||
## 📱 macOS 本地 VPN 配置
|
||||
|
||||
### 方案A:IKEv2(推荐)
|
||||
```
|
||||
系统设置 → 网络 → VPN → 添加 VPN 配置 → IKEv2
|
||||
显示名称: 东京VPN-IKEv2
|
||||
服务器地址: 43.165.178.230
|
||||
远程 ID: 43.165.178.230
|
||||
本地 ID: 留空
|
||||
认证: 用户名
|
||||
用户名: karuo
|
||||
密码: KaruoVPN@2026
|
||||
```
|
||||
**已生成.mobileconfig**: `/Users/karuo/Desktop/东京VPN-IKEv2.mobileconfig` (双击导入)
|
||||
|
||||
### 方案B:L2TP/IPSec(备用)
|
||||
```
|
||||
系统设置 → 网络 → VPN → 添加 VPN 配置 → L2TP/IPSec
|
||||
显示名称: 东京VPN-L2TP
|
||||
服务器地址: 43.165.178.230
|
||||
账号: karuo
|
||||
密码: KaruoVPN@2026
|
||||
共享密钥: KaruoL2TP@2026
|
||||
```
|
||||
|
||||
## 🔧 用户最后一步:换网络
|
||||
**当前本机网络到 43.165.178.230 任何端口都不通**。改用以下任一方式连:
|
||||
- **手机热点**(4G/5G)— 最快,一般直连腾讯东京就行
|
||||
- **公司网络**(quwanzhi.com VPN 内) — 走FRP也能到
|
||||
- **重启路由器/换DNS** — 偶尔本地DNS路由出问题时可恢复
|
||||
|
||||
## 📊 当前进度
|
||||
- ✅ 服务器端 VPN 安装:100%
|
||||
- ✅ 防火墙放行:100%
|
||||
- ✅ macOS 配置生成:100%
|
||||
- ⏳ 本机到服务器网络通: 0%(**用户需换网络**)
|
||||
|
||||
**整体进度 75%**,只剩"换网络"这步用户操作。
|
||||
@@ -1,11 +1,12 @@
|
||||
{
|
||||
"version": 3,
|
||||
"updated": "2026-07-22",
|
||||
"version": 4,
|
||||
"updated": "2026-07-24",
|
||||
"description": "卡若(闽南口音普通话)语音输入→文字的常见ASR误听纠正。卡若AI在内部理解、持久文档、Skill路由与字幕脚本处理前应用;长词优先替换。机制说明见运营中枢/参考资料/闽南话语音_ASR纠错机制.md。",
|
||||
"apply_rules": [
|
||||
"按 corrections 的 key 字符串长度降序做全文替换,避免短词误伤长词",
|
||||
"JSON 中的条目若在 soul_enhance 内置表已有同名 key,以本文件为准(运行时合并覆盖)",
|
||||
"发现新误听:直接把「误听→正写」追加进 corrections,可附 note 说明场景"
|
||||
"发现新误听:直接把「误听→正写」追加进 corrections,可附 note 说明场景",
|
||||
"每轮用户输入必须在意图理解、Skill 路由、工具执行和持久文档写入前完成一次内部 ASR 滤真;命中“纯客宝/纯课宝”时统一使用“存客宝”"
|
||||
],
|
||||
"corrections": {
|
||||
"卡洛创业派对": "卡若创业派对",
|
||||
@@ -154,6 +155,7 @@
|
||||
"2026-06-02 强化:存客宝/触客宝/AI数智员工 全项目**无「流量词」概念**;写需求/代码注释/UI 文案前必须把口述「流量词」滤真为「流量池」;存客宝开发铁律亦同步(见 .cursor/skills/需求协调与归档/SKILL.md 术语铁律)",
|
||||
"2026-07-15 卡若确认:ASR 出现「纯客宝」或「纯课宝」时,一律理解并写为产品名「存客宝」;历史原始聊天可保留原文,新增持久文档必须使用「存客宝」",
|
||||
"2026-07-18:依据公司Mongo最近3000条用户消息复核,新增卡若AI、Skill、Codex、GitHub、Firecrawl、Handy、Webhook、MongoDB、ASR等高频误听;Skier/skyer统一按Skill理解。",
|
||||
"「科舍/科室/克室/课室」不做全局机械替换:开发语境多指Skill,医院或组织语境保留科室,由模型按上下文判断。"
|
||||
"「科舍/科室/克室/课室」不做全局机械替换:开发语境多指Skill,医院或组织语境保留科室,由模型按上下文判断。",
|
||||
"2026-07-24 加固:每轮会话入口、路由与文档写入均执行 ASR 滤真;“纯客宝/纯课宝→存客宝”纳入自动回归用例。"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ python3 "/Users/karuo/Documents/个人/卡若AI/运营中枢/工作台/脚本/ap
|
||||
echo "售上派对运营" | python3 "/Users/karuo/Documents/个人/卡若AI/运营中枢/工作台/脚本/apply_karuo_voice_corrections.py"
|
||||
```
|
||||
|
||||
**Handy 全局语音**:转写由 **G25b Handy中文语音**(SenseVoice + zh-Hans)完成;macOS 通过 `handy_macos_asr_paste.sh` 按官方协议从 stdin 读取转写、从 stdout 返回洗后文,Cursor 每轮仍保留二次兜底。见 `01_卡资(金)/金仓_存储备份/Handy中文语音/SKILL.md`。
|
||||
**Handy 全局语音**:转写由 **G25b Handy中文语音**(SenseVoice + zh-Hans)完成;macOS 通过 `handy_macos_asr_paste.sh` 按官方协议从 stdin 读取转写、从 stdout 返回洗后文,Cursor 每轮仍保留二次兜底。见 `02_卡人(水)/水溪_整理归档/语音输入/SKILL.md`。
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -87,6 +87,14 @@
|
||||
| 拉报告 | 单站:`baidu_tongji_client.py report`;全部网站:`baidu_tongji_client.py report-all --days 30` |
|
||||
| 当前状态 | 私密密钥已落盘;2026-07-19 授权页返回 `invalid_client`,待百度侧识别该 Tongji API 应用后换取令牌 |
|
||||
|
||||
### Vercel
|
||||
|
||||
| 项 | 值 |
|
||||
|---|---|
|
||||
| Token 私密配置 | `03_卡木(木)/木果_项目模板/v0前端生成与预览交付/.env.private` |
|
||||
| 环境变量 | `VERCEL_TOKEN` |
|
||||
| 最近更新 | 2026-07-24,工作手机自动部署专用,作用域 `fnvtk's projects` |
|
||||
|
||||
### v0.dev
|
||||
|
||||
|
||||
|
||||
@@ -516,3 +516,4 @@
|
||||
| 2026-07-19 09:53:08 | 🔄 卡若AI 同步 2026-07-19 09:52 | 更新:总索引与入口、金仓Gitea脚本与配置、水桥平台对接、运营中枢 | 排除 >1MB: 514 个 |
|
||||
| 2026-07-19 10:29:05 | 🔄 卡若AI 同步 2026-07-19 10:29 | 更新:多目录更新 | 排除 >1MB: 515 个 |
|
||||
| 2026-07-19 10:29:31 | 🔄 卡若AI 同步 2026-07-19 10:29 | 更新:更新生成同步说明.py;更新gitea_push_log.md;更新代码管理.md | 排除 >1MB: 515 个 |
|
||||
| 2026-07-24 09:05:49 | 🔄 卡若AI 同步 2026-07-24 09:05 | 更新:更新大文件排除规则;.restore-backups/F22_恢复前_20260723_210051 Skill规则更新;更新SKILL_REGISTRY.md;更新karuo_route.sh;更新SkillSpector扫描处置记录.md;更新对话019f7fde可执行模式复盘.md;更新对话019f7fde通过案例分析.md;更新工具候选与集成规则.md;更新生成业务续执行包.py;更新F22_复扫.json;更新F22_静态扫描.json;更新019f7fde-41d5-7641-9a25-561d9356c193.md;其余1172项见提交文件清单 | 排除 >1MB: 693 个 |
|
||||
|
||||
@@ -520,3 +520,4 @@
|
||||
| 2026-07-19 09:53:08 | 成功 | 成功 | 🔄 卡若AI 同步 2026-07-19 09:52 | 更新:总索引与入口、金仓Gitea脚本与配置、水桥平台对接、运营中枢 | 排除 >1MB: 514 个 | [仓库](http://192.168.110.101:3000/fnvtk/karuo-ai) [百科](http://192.168.110.101:3000/fnvtk/karuo-ai/wiki) |
|
||||
| 2026-07-19 10:29:05 | 成功 | 成功 | 🔄 卡若AI 同步 2026-07-19 10:29 | 更新:多目录更新 | 排除 >1MB: 515 个 | [仓库](http://192.168.110.101:3000/fnvtk/karuo-ai) [百科](http://192.168.110.101:3000/fnvtk/karuo-ai/wiki) |
|
||||
| 2026-07-19 10:29:31 | 成功 | 成功 | 🔄 卡若AI 同步 2026-07-19 10:29 | 更新:更新生成同步说明.py;更新gitea_push_log.md;更新代码管理.md | 排除 >1MB: 515 个 | [仓库](http://192.168.110.101:3000/fnvtk/karuo-ai) [百科](http://192.168.110.101:3000/fnvtk/karuo-ai/wiki) |
|
||||
| 2026-07-24 09:05:49 | 成功 | 成功 | 🔄 卡若AI 同步 2026-07-24 09:05 | 更新:更新大文件排除规则;.restore-backups/F22_恢复前_20260723_210051 Skill规则更新;更新SKILL_REGISTRY.md;更新karuo_route.sh;更新SkillSpector扫描处置记录.md;更新对话019f7fde可执行模式复盘.md;更新对话019f7fde通过案例分析.md;更新工具候选与集成规则.md;更新生成业务续执行包.py;更新F22_复扫.json;更新F22_静态扫描.json;更新019f7fde-41d5-7641-9a25-561d9356c193.md;其余1172项见提交文件清单 | 排除 >1MB: 693 个 | [仓库](http://192.168.110.101:3000/fnvtk/karuo-ai) [百科](http://192.168.110.101:3000/fnvtk/karuo-ai/wiki) |
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
| 3a | CLI/API/SDK/Agent/Hook项目的无界面执行、统一回执和证据闭环 | **F01g** 无界面命令行项目执行闭环 |
|
||||
| 4 | 只维护某站的 1~10 文档 | **F09** 开发文档操控 |
|
||||
| 5 | 脚手架:目录结构 / 模板 | **M03** 项目生成、**M04** 开发模板 |
|
||||
| 6 | v0工程基线、前端视觉与组件标准 | **M11** v0前端生成与预览交付 → **M06** 前端生成 → **F01a** 前端开发 |
|
||||
| 6 | UX/UI、v0工程基线、前端视觉与组件标准 | **M14** 体验设计与视觉规范输出 → **M11** v0前端生成与预览交付 → **M06** 前端生成 → **F01a** 前端开发 |
|
||||
| 7 | 功能做完验收 | **F01b** 全栈测试 |
|
||||
| 8 | Bug / 疑难 | **F05** 代码修复 + **F05a** 编码陷阱(写码前快检);需求不清 → **F06** 智能追问 |
|
||||
| 9 | 本机跑起来、端口不冲突 | **F22** 本地项目启动、**G21** 端口登记 |
|
||||
@@ -47,7 +47,8 @@
|
||||
|
||||
| 模块 | 编号 | 技能 | SKILL 路径 |
|
||||
|:---|:---:|:---|:---|
|
||||
| 毛玻璃与前端标准 | F01a | 前端开发 | `04_卡火(火)/火炬_全栈消息/前端开发/SKILL.md` |
|
||||
| UX/UI、状态机、Design Tokens、a11y | M14 | 体验设计与视觉规范输出(卡前) | `03_卡木(木)/卡前_体验设计/体验设计与视觉规范输出/SKILL.md` |
|
||||
| 毛玻璃与前端工程标准 | F01a | 前端开发(卡前/火炬) | `04_卡火(火)/火炬_全栈消息/前端开发/SKILL.md` |
|
||||
| 快速出页面 | M06 | 前端生成 | `03_卡木(木)/木果_项目模板/前端生成/SKILL.md` |
|
||||
| v0通用工程基线、Git/Preview/PR交付 | M11 | v0前端生成与预览交付 | `03_卡木(木)/木果_项目模板/v0前端生成与预览交付/SKILL.md` |
|
||||
| 架构图 / draw.io 风 | M08 | Next AI Draw | `03_卡木(木)/木果_项目模板/Next AI Draw/SKILL.md` |
|
||||
@@ -183,4 +184,4 @@
|
||||
|
||||
---
|
||||
|
||||
*最后更新:2026-07-22(统一 v0 前端工程基线与开发文档说明)*
|
||||
*最后更新:2026-07-26(新增卡前:UX/UI、状态机、Design Tokens、a11y 与 v0/Vercel 体验交付总控)*
|
||||
|
||||
20
运营中枢/工作台/脚本/test_karuo_asr_corrections.py
Executable file
20
运营中枢/工作台/脚本/test_karuo_asr_corrections.py
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env python3
|
||||
"""卡若AI ASR 高频产品名回归测试。"""
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
|
||||
root = Path(__file__).resolve().parents[3]
|
||||
module_path = root / '运营中枢' / '工作台' / '脚本' / 'apply_karuo_voice_corrections.py'
|
||||
spec = importlib.util.spec_from_file_location('asr', module_path)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
|
||||
cases = {
|
||||
'纯客宝更新一下登录流程': '存客宝更新一下登录流程',
|
||||
'纯课宝的接口要检查': '存客宝的接口要检查',
|
||||
'卡罗AI连接纯客宝': '卡若AI连接存客宝',
|
||||
}
|
||||
for raw, expected in cases.items():
|
||||
actual = module.apply_corrections(raw)
|
||||
assert actual == expected, (raw, actual, expected)
|
||||
print(f'PASS {len(cases)} ASR regression cases')
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"version": "1.0",
|
||||
"updatedAt": "2026-07-19T00:00:00+08:00",
|
||||
"updatedAt": "2026-07-26T00:00:00+08:00",
|
||||
"source": "卡若AI",
|
||||
"model": {
|
||||
"orchestrator": 1,
|
||||
@@ -907,14 +907,15 @@
|
||||
"group": "木",
|
||||
"color": "#2E8B57",
|
||||
"role": "木果核心执行Agent",
|
||||
"mission": "模板、PPT、前端形态和品牌叙事",
|
||||
"mission": "模板、PPT、整站视觉采集、产品设计资产和品牌叙事;前端体验交付协同卡前",
|
||||
"personality": {
|
||||
"mbti": "ESTP",
|
||||
"pdp": "老虎+I"
|
||||
},
|
||||
"workModes": [
|
||||
"塑形",
|
||||
"木芽表达模式"
|
||||
"内容资产",
|
||||
"品牌叙事"
|
||||
],
|
||||
"skillMounts": [
|
||||
"M03",
|
||||
@@ -970,6 +971,88 @@
|
||||
"status": "active",
|
||||
"personaPath": "03_卡木(木)/木果_项目模板/AGENT.md"
|
||||
},
|
||||
{
|
||||
"id": "kaqian",
|
||||
"name": "卡前",
|
||||
"agentType": "member",
|
||||
"owner": "卡木",
|
||||
"group": "木",
|
||||
"color": "#2E8B57",
|
||||
"role": "UX/UI视觉与交互设计师、前端体验交付负责人",
|
||||
"mission": "统一产品信息架构、交互状态、设计系统、无障碍与v0/Vercel体验交付",
|
||||
"personality": {
|
||||
"mbti": "ENFP",
|
||||
"pdp": "老虎25+猫头鹰18",
|
||||
"disc": "D力量17+C完美15"
|
||||
},
|
||||
"workModes": [
|
||||
"用户旅程",
|
||||
"体验设计",
|
||||
"设计系统",
|
||||
"前端交付验收"
|
||||
],
|
||||
"skillMounts": [
|
||||
"M14",
|
||||
"M06",
|
||||
"M11",
|
||||
"M13",
|
||||
"F01a",
|
||||
"F01f",
|
||||
"M02e"
|
||||
],
|
||||
"mcpPolicy": {
|
||||
"mode": "inherit",
|
||||
"editable": true
|
||||
},
|
||||
"mcpBindings": [
|
||||
"figma",
|
||||
"browser",
|
||||
"image",
|
||||
"filesystem",
|
||||
"github",
|
||||
"v0-vercel-bridge"
|
||||
],
|
||||
"hooks": [
|
||||
{
|
||||
"event": "before_input",
|
||||
"handler": "karuo_asr_normalize",
|
||||
"description": "应用卡若ASR纠错和术语归一"
|
||||
},
|
||||
{
|
||||
"event": "before_route",
|
||||
"handler": "karuo_skill_route",
|
||||
"description": "查询Agent与Skill注册表"
|
||||
},
|
||||
{
|
||||
"event": "before_execute",
|
||||
"handler": "karuo_scope_guard",
|
||||
"description": "检查写入范围、依赖和验收口径"
|
||||
},
|
||||
{
|
||||
"event": "after_execute",
|
||||
"handler": "karuo_verify",
|
||||
"description": "执行至少两轮结果验证"
|
||||
},
|
||||
{
|
||||
"event": "after_response",
|
||||
"handler": "karuo_recap",
|
||||
"description": "生成卡若复盘"
|
||||
},
|
||||
{
|
||||
"event": "after_response",
|
||||
"handler": "karuo_mongo_sync",
|
||||
"description": "增量留存对话与执行证据"
|
||||
}
|
||||
],
|
||||
"aliases": [
|
||||
"前端体验",
|
||||
"UX/UI",
|
||||
"体验设计",
|
||||
"界面设计"
|
||||
],
|
||||
"status": "active",
|
||||
"personaPath": "03_卡木(木)/卡前_体验设计/AGENT.md"
|
||||
},
|
||||
{
|
||||
"id": "mushi",
|
||||
"name": "木识",
|
||||
|
||||
Reference in New Issue
Block a user