@gavinc-cn/touchstone-dsh 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +0 -0
- package/README.md +22 -1
- package/README.zh.md +12 -1
- package/agents.py +0 -0
- package/auth.py +0 -0
- package/board.py +1119 -93
- package/builtin_assets.py +0 -0
- package/builtin_prompts/free_style/flow.md +0 -0
- package/builtin_prompts/free_style/load_test.md +0 -0
- package/builtin_prompts/free_style/templates/bug_report_template.md +0 -0
- package/builtin_prompts/free_style/templates/case_template.md +0 -0
- package/builtin_prompts/free_style/templates/index_template.md +0 -0
- package/builtin_prompts/free_style/templates/load_charts_template.md +0 -0
- package/builtin_prompts/free_style/templates/load_index_template.md +0 -0
- package/builtin_prompts/free_style/templates/load_plan_template.md +0 -0
- package/builtin_prompts/free_style/templates/load_scenario_template.md +0 -0
- package/builtin_prompts/free_style/templates/status_template.md +0 -0
- package/chat.py +590 -17
- package/db.py +63 -12
- package/dsh-plugin/cordis.patch.yml +7 -3
- package/dsh-plugin/install.sh +8 -5
- package/dsh-plugin/lib/agent-driver.js +895 -126
- package/dsh-plugin/lib/client.bundle.js +94 -9
- package/dsh-plugin/lib/index.js +399 -20
- package/dsh-plugin/scripts/build.mjs +0 -0
- package/dsh-plugin/scripts/prepack-check.mjs +0 -0
- package/dsh-plugin/scripts/select-bundle.mjs +0 -0
- package/dsh-plugin/src/client.js +94 -9
- package/dshdriver.py +74 -5
- package/dshevents.py +100 -6
- package/export_cases.py +0 -0
- package/extensions/dsh-autocommit/asset.json +0 -0
- package/extensions/dsh-autocommit/pkg/lib/index.mjs +0 -0
- package/extensions/dsh-autocommit/pkg/package.json +0 -0
- package/feishu.py +1226 -42
- package/feishu_conv.py +920 -0
- package/lib.py +0 -0
- package/lifecycle.py +0 -0
- package/loadcase.py +0 -0
- package/loadgen.py +0 -0
- package/localbus.py +0 -0
- package/migrate_p7b.py +0 -0
- package/package.json +8 -3
- package/platcompat.py +0 -0
- package/prompts.py +0 -0
- package/rag.py +0 -0
- package/requirements.txt +2 -1
- package/runner.py +11 -0
- package/server.py +264 -30
- package/sessparse.py +244 -44
- package/touchstone.py +8 -4
- package/waitq.py +23 -0
- package/webui/dist-plugin/assets/index-JLwyZZxC.js +484 -0
- package/webui/dist-plugin/assets/index-tJ0TlvQ3.css +1 -0
- package/webui/dist-plugin/index.html +2 -2
- package/worktree.py +100 -0
- package/webui/dist-plugin/assets/index-B0WoP1Qy.css +0 -1
- package/webui/dist-plugin/assets/index-eHpxBfP6.js +0 -473
package/db.py
CHANGED
|
@@ -206,11 +206,18 @@ CREATE TABLE IF NOT EXISTS feishu_user_cfgs (
|
|
|
206
206
|
user_id INTEGER PRIMARY KEY,
|
|
207
207
|
json TEXT NOT NULL DEFAULT '{}'
|
|
208
208
|
);
|
|
209
|
+
-- 飞书号绑定(一行 = 一个飞书号 ↔ 一个 Touchstone 用户);后四列是「当前会话」
|
|
210
|
+
-- 绑定态(2026-10-08 飞书通用对话):私聊普通文本按它路由到 agent 会话,
|
|
211
|
+
-- 换绑/解绑一律归零(见 set_feishu_binding 的「换绑归零」语义)
|
|
209
212
|
CREATE TABLE IF NOT EXISTS feishu_bindings (
|
|
210
213
|
open_id TEXT PRIMARY KEY,
|
|
211
214
|
user_id INTEGER NOT NULL UNIQUE,
|
|
212
215
|
default_project_id INTEGER NOT NULL DEFAULT 0,
|
|
213
|
-
bound_at TEXT NOT NULL DEFAULT (datetime('now', 'localtime'))
|
|
216
|
+
bound_at TEXT NOT NULL DEFAULT (datetime('now', 'localtime')),
|
|
217
|
+
cur_sid TEXT NOT NULL DEFAULT '', -- 当前会话 id(空=未绑定会话)
|
|
218
|
+
cur_project_id INTEGER NOT NULL DEFAULT 0, -- 当前会话所属项目(投递队列归属)
|
|
219
|
+
cur_title TEXT NOT NULL DEFAULT '', -- 绑定时标题快照
|
|
220
|
+
cur_bound_at TEXT NOT NULL DEFAULT '' -- 绑定时刻(localtime)
|
|
214
221
|
);
|
|
215
222
|
CREATE INDEX IF NOT EXISTS idx_board_cards_project ON board_cards(project_id);
|
|
216
223
|
CREATE INDEX IF NOT EXISTS idx_board_comments_card ON board_comments(card_id);
|
|
@@ -491,6 +498,17 @@ CREATE UNIQUE INDEX idx_projects_user_name ON projects(user_id, name);
|
|
|
491
498
|
if "user_id" not in cols:
|
|
492
499
|
conn.execute("ALTER TABLE feishu_outbox"
|
|
493
500
|
" ADD COLUMN user_id INTEGER NOT NULL DEFAULT 0")
|
|
501
|
+
# feishu_bindings 补「当前会话」四列(2026-10-08 飞书通用对话:绑定到某个会话)。
|
|
502
|
+
# 旧库的 feishu_bindings 由 CREATE TABLE IF NOT EXISTS 原样保留(不含这四列),
|
|
503
|
+
# 只能在此 ALTER 补齐;幂等:列已存在则跳过,存量行取空默认值(未绑定会话)。
|
|
504
|
+
with connect() as conn:
|
|
505
|
+
cols = {r["name"] for r in conn.execute("PRAGMA table_info(feishu_bindings)")}
|
|
506
|
+
for name, ddl in (("cur_sid", "TEXT NOT NULL DEFAULT ''"),
|
|
507
|
+
("cur_project_id", "INTEGER NOT NULL DEFAULT 0"),
|
|
508
|
+
("cur_title", "TEXT NOT NULL DEFAULT ''"),
|
|
509
|
+
("cur_bound_at", "TEXT NOT NULL DEFAULT ''")):
|
|
510
|
+
if name not in cols:
|
|
511
|
+
conn.execute(f"ALTER TABLE feishu_bindings ADD COLUMN {name} {ddl}")
|
|
494
512
|
# 旧全局飞书配置迁移(2026-09-09 起飞书设置用户级):旧的 app_settings['feishu']
|
|
495
513
|
# 由 admin 在后台管理页配置,迁移给 admin 自己的用户配置(已有私户配置则不覆盖)
|
|
496
514
|
with connect() as conn:
|
|
@@ -570,13 +588,17 @@ def _ensure_users_columns(conn):
|
|
|
570
588
|
def seed_admin():
|
|
571
589
|
"""无任何用户时插入 admin,返回是否实际创建(bool,供启动横幅决定是否提示)。
|
|
572
590
|
|
|
573
|
-
|
|
591
|
+
口令来源(按优先级):
|
|
574
592
|
- 环境变量 TS_ADMIN_PASSWORD 非空:用之(部署方自定义初始口令,视为有意设定,
|
|
575
593
|
不强制改密)——保持 CI/隔离实例夹具的既有行为;
|
|
576
|
-
-
|
|
594
|
+
- 环境变量 TS_ADMIN_PASSWORDLESS=1(插件形态,薄壳下发,2026-10-08):种**空口令**
|
|
595
|
+
admin、不强制改密。面板本来就是免登 admin(信任头),口令只用于独立形态/直连
|
|
596
|
+
登录;用户之后在设置页设了密码就按设置的来(change_password 存 MD5);
|
|
597
|
+
- 都没有:用 secrets 随机生成 16 位一次性初始口令(URL-safe),并置
|
|
577
598
|
must_change_pw=1,登录后必须先改密才能调用其他 API。
|
|
578
599
|
随机口令经 take_seed_password() 交启动横幅打印一次,不落库明文、不常态打印。
|
|
579
|
-
PBKDF2 哈希 +
|
|
600
|
+
哈希:自定义口令/随机口令沿用 PBKDF2 哈希 + 随机盐(哈希算法为需求约定,勿改);
|
|
601
|
+
空口令走 auth.md5_password("")(与「新写入密码统一 MD5」同口径,且登录时空串即通过)。
|
|
580
602
|
"""
|
|
581
603
|
global _SEED_PASSWORD
|
|
582
604
|
import auth
|
|
@@ -587,10 +609,12 @@ def seed_admin():
|
|
|
587
609
|
_ensure_users_columns(conn)
|
|
588
610
|
env_pw = os.environ.get("TS_ADMIN_PASSWORD")
|
|
589
611
|
if env_pw:
|
|
590
|
-
password, must_change = env_pw, 0
|
|
612
|
+
password, must_change, seed_hash = env_pw, 0, None
|
|
613
|
+
elif os.environ.get("TS_ADMIN_PASSWORDLESS") == "1":
|
|
614
|
+
password, must_change, seed_hash = "", 0, auth.md5_password("")
|
|
591
615
|
else:
|
|
592
|
-
password, must_change = secrets.token_urlsafe(12), 1
|
|
593
|
-
pw_hash, salt = auth.hash_password(password)
|
|
616
|
+
password, must_change, seed_hash = secrets.token_urlsafe(12), 1, None
|
|
617
|
+
pw_hash, salt = seed_hash or auth.hash_password(password)
|
|
594
618
|
conn.execute(
|
|
595
619
|
"INSERT INTO users(username, pass_hash, salt, must_change_pw, created_at)"
|
|
596
620
|
" VALUES(?,?,?,?,?)",
|
|
@@ -1089,7 +1113,7 @@ def update_board_card(card_id, mark_unread=True, **fields):
|
|
|
1089
1113
|
|
|
1090
1114
|
|
|
1091
1115
|
def mark_card_viewed(card_id):
|
|
1092
|
-
"""
|
|
1116
|
+
"""清卡片「状态有更新·用户未打开」标记(用户打开卡片详情 / 点卡面操作行按钮时调用)。
|
|
1093
1117
|
|
|
1094
1118
|
只写 `unread=0`,**刻意不刷 updated_at**:查看不是卡片内容变更,顺带刷时间
|
|
1095
1119
|
会污染「更新新→旧」排序口径与 `list_queued_board_cards` 的排队顺序(该查询
|
|
@@ -1377,13 +1401,18 @@ def get_feishu_binding_by_user(user_id):
|
|
|
1377
1401
|
|
|
1378
1402
|
def set_feishu_binding(open_id, user_id, default_project_id=0):
|
|
1379
1403
|
"""绑定飞书号 ↔ 用户(双向一对一):user_id 已绑其他 open_id 时先清旧绑
|
|
1380
|
-
(换号重绑语义);open_id 重绑直接覆盖(UPDATE)。
|
|
1404
|
+
(换号重绑语义);open_id 重绑直接覆盖(UPDATE)。
|
|
1405
|
+
|
|
1406
|
+
2026-10-08 起「换绑归零」:写入与覆盖都把「当前会话」四列清空——绑定态不能
|
|
1407
|
+
跨身份继承(同一用户换飞书号、同一飞书号换用户,都必须重新显式绑定会话)。"""
|
|
1381
1408
|
with connect() as conn:
|
|
1382
1409
|
conn.execute("DELETE FROM feishu_bindings WHERE user_id=?", (user_id,))
|
|
1383
1410
|
conn.execute(
|
|
1384
|
-
"INSERT INTO feishu_bindings(open_id, user_id, default_project_id
|
|
1385
|
-
"
|
|
1386
|
-
"
|
|
1411
|
+
"INSERT INTO feishu_bindings(open_id, user_id, default_project_id,"
|
|
1412
|
+
" cur_sid, cur_project_id, cur_title, cur_bound_at)"
|
|
1413
|
+
" VALUES(?,?,?,'',0,'','') ON CONFLICT(open_id) DO UPDATE SET"
|
|
1414
|
+
" user_id=excluded.user_id, default_project_id=excluded.default_project_id,"
|
|
1415
|
+
" cur_sid='', cur_project_id=0, cur_title='', cur_bound_at=''",
|
|
1387
1416
|
(open_id, user_id, default_project_id))
|
|
1388
1417
|
|
|
1389
1418
|
|
|
@@ -1405,3 +1434,25 @@ def set_feishu_default_project(open_id, project_id):
|
|
|
1405
1434
|
conn.execute(
|
|
1406
1435
|
"UPDATE feishu_bindings SET default_project_id=? WHERE open_id=?",
|
|
1407
1436
|
(project_id, open_id))
|
|
1437
|
+
|
|
1438
|
+
|
|
1439
|
+
def set_feishu_cur_session(open_id, project_id, sid, title=""):
|
|
1440
|
+
"""写「当前会话」绑定(open_id 行必须已存在;时刻取本地时间字符串)。
|
|
1441
|
+
|
|
1442
|
+
当前会话 = 该飞书号私聊普通文本的投递目标:`cur_sid` 空表示未绑定(走默认
|
|
1443
|
+
项目/引导指令),`cur_project_id` 是投递队列的归属项目。sid/title 传空即等价
|
|
1444
|
+
于清空对应字段(None 也按空串落库,避免行上出现 NULL)。
|
|
1445
|
+
"""
|
|
1446
|
+
with connect() as conn:
|
|
1447
|
+
conn.execute(
|
|
1448
|
+
"UPDATE feishu_bindings SET cur_sid=?, cur_project_id=?, cur_title=?,"
|
|
1449
|
+
" cur_bound_at=? WHERE open_id=?",
|
|
1450
|
+
(sid or "", int(project_id or 0), title or "", now_str(), open_id))
|
|
1451
|
+
|
|
1452
|
+
|
|
1453
|
+
def clear_feishu_cur_session(open_id):
|
|
1454
|
+
"""清「当前会话」绑定(保留默认项目;未绑定时为 no-op)。"""
|
|
1455
|
+
with connect() as conn:
|
|
1456
|
+
conn.execute(
|
|
1457
|
+
"UPDATE feishu_bindings SET cur_sid='', cur_project_id=0,"
|
|
1458
|
+
" cur_title='', cur_bound_at='' WHERE open_id=?", (open_id,))
|
|
@@ -8,14 +8,18 @@
|
|
|
8
8
|
# 面板里就会出现 Touchstone 卡片与该行开关(开关写回 profile patch 的 disabled)。
|
|
9
9
|
#
|
|
10
10
|
# config 约定: repoDir 是**机器相关**路径(Touchstone 平台根,须含 server.py 与 webui/dist-plugin),
|
|
11
|
-
# 不写死在这里 —— 由各 profile 的 cordis.patch.yml 用 `- id: touchstone` + `config:` 覆盖提供
|
|
11
|
+
# 不写死在这里 —— 由各 profile 的 cordis.patch.yml 用 `- id: touchstone-dsh` + `config:` 覆盖提供
|
|
12
12
|
# (覆盖是整键替换,且 profile 层后于 bundle 层应用)。**2026-10-07 起 repoDir 可省**:
|
|
13
13
|
# 包已自包含(Python 平台 + 预构建 webui/dist-plugin 都在包内),薄壳缺省回退到**包自身目录**
|
|
14
14
|
# (lib/index.js 里 PACKAGE_DIR),只有要指向别的检出、或指定 pythonPath 时才写 config。
|
|
15
15
|
# pythonPath 缺省 python3。
|
|
16
16
|
#
|
|
17
17
|
# name 必须是**完整包名**(与仓库根 package.json 的 name、scripts/build.mjs 生成的
|
|
18
|
-
# client.bundle.js 里的模块 id 三者逐字一致);条目 id
|
|
18
|
+
# client.bundle.js 里的模块 id 三者逐字一致);条目 id 是 touchstone-dsh(= 包名去掉 scope)。
|
|
19
|
+
# id 只是组合树里的**本地行键**(profile 覆盖行按它命中、Plugins 面板行开关按它写回),
|
|
20
|
+
# **不是包名**,改它必须与各 profile 的覆盖行同步 —— 否则覆盖落空(repoDir/pythonPath/disabled
|
|
21
|
+
# 全丢,回落包自身目录 + python3),面板开关还会在 profile patch 里追加一行、把旧行留成孤儿。
|
|
22
|
+
# 2026-10-09 由旧名 touchstone 改名(旧名是 2026-10-07 包改名前的残留)。
|
|
19
23
|
- insert:
|
|
20
|
-
- id: touchstone
|
|
24
|
+
- id: touchstone-dsh
|
|
21
25
|
name: "@gavinc-cn/touchstone-dsh"
|
package/dsh-plugin/install.sh
CHANGED
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
#
|
|
16
16
|
# 机器相关配置(repoDir / pythonPath)**可省**(2026-10-07 自包含包起): 缺省 repoDir = 包自身目录,
|
|
17
17
|
# 包里就有 server.py 与 webui/dist-plugin; 只有要指向别的检出、或指定解释器时才在 profile 覆盖:
|
|
18
|
-
# - id: touchstone
|
|
19
|
-
# config: { pythonPath: <解释器> } # 条目 id
|
|
18
|
+
# - id: touchstone-dsh
|
|
19
|
+
# config: { pythonPath: <解释器> } # 条目 id 是 touchstone-dsh(= 包名去掉 scope)
|
|
20
20
|
# 环境变量: TS_DSH_PROFILE(默认 web) / TS_DSH_PYTHON(显式指定解释器, 优先)
|
|
21
21
|
set -euo pipefail
|
|
22
22
|
|
|
@@ -27,9 +27,11 @@ PROFILE_DIR="$HOME/.dsh/profiles/$PROFILE"
|
|
|
27
27
|
PKG_NAME="$(node -p "require('$REPO_DIR/package.json').name")"
|
|
28
28
|
|
|
29
29
|
# 解释器探测链(2026-10-02 去硬编码,原写死某个 conda 环境的解释器绝对路径):
|
|
30
|
-
# TS_DSH_PYTHON / TS_PYTHON 显式指定 → 常见 conda 环境 → PATH 上的 python3 → 报错;
|
|
30
|
+
# TS_DSH_PYTHON / TS_PYTHON 显式指定 → 常见 conda 环境 → PATH 上的 python → python3 → 报错;
|
|
31
31
|
# conda/PATH 候选里优先「能导入运行依赖(zstandard/requests)」的那个, 避免选中
|
|
32
32
|
# 同机其他缺依赖的环境; 全都不满足时回落到首个存在的解释器。
|
|
33
|
+
# PATH 档的顺序 2026-10-11 改为 python 优先(用户口径「兜底应该用 python」), 与插件薄壳
|
|
34
|
+
# lib/index.js 的 PYTHON_CANDIDATES 同序; Linux 上两者通常都在, 改序不改变命中结果。
|
|
33
35
|
_depline() { "$1" -c 'import zstandard, requests' >/dev/null 2>&1; }
|
|
34
36
|
|
|
35
37
|
pick_python() {
|
|
@@ -39,6 +41,7 @@ pick_python() {
|
|
|
39
41
|
done
|
|
40
42
|
for cand in "$HOME"/{mini,ana}conda3/envs/*/bin/python3 \
|
|
41
43
|
/opt/{mini,ana}conda3/envs/*/bin/python3 \
|
|
44
|
+
"$(command -v python || true)" \
|
|
42
45
|
"$(command -v python3 || true)"; do
|
|
43
46
|
[[ -n "$cand" && -x "$cand" ]] || continue
|
|
44
47
|
[[ -n "$fallback" ]] || fallback="$cand"
|
|
@@ -48,7 +51,7 @@ pick_python() {
|
|
|
48
51
|
return 1
|
|
49
52
|
}
|
|
50
53
|
PYTHON_PATH="$(pick_python)" || {
|
|
51
|
-
echo "错误: 未找到可用的 python3
|
|
54
|
+
echo "错误: 未找到可用的 Python 解释器(依次试过 python、python3);请用 TS_DSH_PYTHON=/path/to/python 显式指定" >&2
|
|
52
55
|
exit 1
|
|
53
56
|
}
|
|
54
57
|
|
|
@@ -75,5 +78,5 @@ echo "完成(包名 $PKG_NAME)。重启 dsh web 生效(package.json 变更
|
|
|
75
78
|
echo "之后侧栏 Plugins 面板里会出现 Touchstone 卡片与该行开关。"
|
|
76
79
|
echo "repoDir 不再必填(缺省 = 包自身目录, 包里自带 Python 平台与预构建前端);"
|
|
77
80
|
echo "只有要指向本机开发检出、或指定解释器时才在 $PROFILE_DIR/cordis.patch.yml 里加:"
|
|
78
|
-
echo " - id: touchstone"
|
|
81
|
+
echo " - id: touchstone-dsh"
|
|
79
82
|
echo " config: { repoDir: $REPO_DIR, pythonPath: $PYTHON_PATH }"
|