sophhub 0.4.57 → 0.4.59

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.
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.0.3",
2
+ "version": "1.0.4",
3
3
  "agent_id": "ai-dept-admin",
4
4
  "description": "部门管理主 Agent:部门知识库、人员档案与绩效、会议(单次/周期/查询)、待办与复盘记录",
5
5
  "bot_api_enabled": false,
@@ -33,5 +33,5 @@
33
33
  "auto_install": true
34
34
  }
35
35
  ],
36
- "llm": "GLM-5"
36
+ "llm": "DeepSeek-V4-Pro"
37
37
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sophhub",
3
- "version": "0.4.57",
3
+ "version": "0.4.59",
4
4
  "description": "SophHub CLI - Manage and download AI Agent skills and agents",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,12 +1,33 @@
1
1
  {
2
2
  "name": "agent-install",
3
- "version": "0.1.12",
3
+ "version": "0.1.15",
4
4
  "types": [
5
5
  "store"
6
6
  ],
7
7
  "displayName": "Agent安装",
8
8
  "description": "安装或升级 Sophclaw Agent(含占位替换、备份、post_install 脚本与自动安装 skill)。",
9
9
  "changelog": [
10
+ {
11
+ "changes": [
12
+ "修复 update_openclaw.py 新建实例时覆盖原实例的严重 BUG:改为按目标 openclaw_id 定位条目,新建时追加、不覆盖同源原实例"
13
+ ],
14
+ "date": "2026-07-09",
15
+ "version": "0.1.15"
16
+ },
17
+ {
18
+ "changes": [
19
+ "安装汇总以 openclaw_id(实例标识)为主,不再展示源 agent_id,避免与实例标识混淆"
20
+ ],
21
+ "date": "2026-07-09",
22
+ "version": "0.1.14"
23
+ },
24
+ {
25
+ "changes": [
26
+ "新增 resolve_new_instance.py:新建实例时确定生成下一个可用的 openclaw_id 和 workspace,不再依赖 LLM 手动生成"
27
+ ],
28
+ "date": "2026-07-09",
29
+ "version": "0.1.13"
30
+ },
10
31
  {
11
32
  "changes": [
12
33
  "新建实例时仅询问 agent_name,openclaw_id 和 workspace 按规则自动生成"
@@ -61,9 +61,15 @@ uv run {baseDir}/scripts/resolve_install_params.py \
61
61
 
62
62
  确认 `openclaw_id`、`workspace`、`agent_name`。有 `placeholder_catalog` 时让用户确认替换文案,用户可覆盖默认值。
63
63
 
64
- 用户选择「新建实例」时,仅询问 `agent_name`(显示名称),`openclaw_id` 和 `workspace` 按规则自动生成:
65
- - `openclaw_id`:`{agent_id}-{N}`,N 为递增序号(如已存在 `agent_id` 和 `agent_id-2`,则用 `agent_id-3`)
66
- - `workspace`:基础 workspace 路径末尾追加 `-{N}`(如 `/home/node/.openclaw/workspace-dept-2`)
64
+ 用户选择「新建实例」时,**仅询问 `agent_name`**(显示名称),`openclaw_id` 和 `workspace` 由脚本确定生成,不向用户索要:
65
+
66
+ ```bash
67
+ uv run {baseDir}/scripts/resolve_new_instance.py \
68
+ --agent-id "{agent_id}" \
69
+ --path "/home/node/.openclaw/workspace"
70
+ ```
71
+
72
+ 取返回的 `openclaw_id`(形如 `{agent_id}-{N}`)和 `workspace`(实例 #1 workspace 末尾追加 `-{N}`),后续步骤按此传入 `--openclaw-id` / `--workspace`。
67
73
 
68
74
  **2)按需替换 .md 中的占位符**(有 `placeholders` 时)
69
75
 
@@ -158,7 +164,7 @@ uv run {baseDir}/scripts/update_openclaw.py \
158
164
 
159
165
  脚本自动处理 `auto_generate_image_description`、`heartbeat`(仅当 `.config.json` 显式配置时写入)、`bot-api` 账号等,输出中包含对应字段便于核对。
160
166
 
161
- 完成后汇总:agent_id、结果(新装/升级/重置)、workspace、备份目录、skill 列表、bot-api(密钥走安全渠道)。
167
+ 完成后汇总(以实例标识 `openclaw_id` 为主,不展示源 `agent_id` 以免与实例标识混淆):openclaw_id、结果(新装/升级/重置/新建实例)、workspace、显示名称、备份目录、skill 列表、bot-api(密钥走安全渠道)。
162
168
 
163
169
  ---
164
170
 
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env python3
2
+ """为「新建实例」确定性地生成下一个可用的 openclaw_id 和 workspace。
3
+
4
+ 读取 openclaw.json,扫描 agents.list 中所有匹配 ^{agent_id}(-(\d+))?$ 的条目,
5
+ 取最大编号 + 1 作为新实例编号,输出 openclaw_id 与 workspace。
6
+ """
7
+ from __future__ import annotations
8
+
9
+ import argparse
10
+ import json
11
+ import re
12
+ import sys
13
+ from pathlib import Path
14
+
15
+ from common import (
16
+ default_openclaw_config_path,
17
+ find_agent_entry,
18
+ load_agent_definition,
19
+ load_openclaw_config,
20
+ )
21
+
22
+
23
+ def next_instance_number(existing_ids: list[str], agent_id: str) -> int:
24
+ """从已有 id 列表算出下一个实例编号。裸 agent_id 视为 1,agent_id-N 视为 N。"""
25
+ pattern = re.compile(rf"^{re.escape(agent_id)}(?:-(\d+))?$")
26
+ numbers: set[int] = set()
27
+ for eid in existing_ids:
28
+ if not isinstance(eid, str):
29
+ continue
30
+ match = pattern.fullmatch(eid)
31
+ if match is None:
32
+ continue
33
+ suffix = match.group(1)
34
+ numbers.add(int(suffix) if suffix else 1)
35
+ if not numbers:
36
+ return 2
37
+ return max(numbers) + 1
38
+
39
+
40
+ def base_workspace_for(agent_id: str, agent_def: dict, config: dict) -> str:
41
+ """实例 #1 的实际 workspace 作为新实例 workspace 的基。取不到则回退到 .config.json 的 workspace。"""
42
+ entry = find_agent_entry(config, agent_id)
43
+ if entry and isinstance(entry.get("workspace"), str) and entry["workspace"].strip():
44
+ return entry["workspace"].rstrip("/")
45
+ configured = agent_def.get("install", {}).get("workspace") or agent_def.get("workspace")
46
+ if isinstance(configured, str) and configured.strip():
47
+ return configured.rstrip("/")
48
+ return f"/home/node/.openclaw/workspace-{agent_id}"
49
+
50
+
51
+ def resolve_new_instance(agent_id: str, source_path: Path, config_path: Path) -> dict[str, object]:
52
+ agent_def = load_agent_definition(agent_id, source_path)
53
+ openclaw = load_openclaw_config(config_path)
54
+ agents_list = openclaw.get("agents", {}).get("list", []) or []
55
+ existing_ids = [a.get("id") for a in agents_list if isinstance(a, dict)]
56
+
57
+ next_n = next_instance_number(existing_ids, agent_id)
58
+ new_openclaw_id = f"{agent_id}-{next_n}"
59
+ base_ws = base_workspace_for(agent_id, agent_def, openclaw)
60
+ new_workspace = f"{base_ws}-{next_n}"
61
+
62
+ return {
63
+ "ok": True,
64
+ "agent_id": agent_id,
65
+ "openclaw_id": new_openclaw_id,
66
+ "workspace": new_workspace,
67
+ "instance_number": next_n,
68
+ "message": f"新实例参数:openclaw_id={new_openclaw_id},workspace={new_workspace}",
69
+ }
70
+
71
+
72
+ def main() -> int:
73
+ parser = argparse.ArgumentParser(description="为新建实例生成下一个可用的 openclaw_id 和 workspace")
74
+ parser.add_argument("--agent-id", required=True, help="Agent ID")
75
+ parser.add_argument("--path", required=True, help="Agent 下载目录(sophhub agent download 的 --path)")
76
+ parser.add_argument(
77
+ "--config",
78
+ default=str(default_openclaw_config_path()),
79
+ help="openclaw.json 路径",
80
+ )
81
+ args = parser.parse_args()
82
+
83
+ result = resolve_new_instance(
84
+ args.agent_id,
85
+ Path(args.path).expanduser().resolve(),
86
+ Path(args.config).expanduser().resolve(),
87
+ )
88
+ json.dump(result, sys.stdout, indent=2, ensure_ascii=False)
89
+ sys.stdout.write("\n")
90
+ return 0
91
+
92
+
93
+ if __name__ == "__main__":
94
+ raise SystemExit(main())
@@ -14,6 +14,7 @@ from common import (
14
14
  deep_merge_dict,
15
15
  default_openclaw_config_path,
16
16
  dump_json,
17
+ entry_matches_source_agent,
17
18
  find_agent_entry,
18
19
  find_bot_api_accounts_by_agent_id,
19
20
  find_agent_by_workspace,
@@ -165,15 +166,18 @@ def update_openclaw(
165
166
  config = load_openclaw_config(config_path)
166
167
  main_migration = _ensure_main_agent_in_list(config, config_path)
167
168
  desired_workspace = resolved_workspace or agent_def.get("install", {}).get("workspace") or agent_def.get("workspace")
168
- previous = resolve_existing_agent_entry(config_path, config, agent_id, desired_workspace)
169
- if previous is None and resolved_openclaw_id:
170
- existing_by_target_id = find_agent_entry(config, resolved_openclaw_id)
171
- if existing_by_target_id is not None:
172
- raise ValueError(f"目标 openclaw id 已存在且不属于当前 Agent: {resolved_openclaw_id}")
173
- if previous is not None:
174
- target_agent_id = previous.get("id") if previous.get("id") else agent_id
169
+ # 确定目标实例 id:显式传入的 openclaw_id 优先(支持新建/指定实例更新)
170
+ resolved_previous = resolve_existing_agent_entry(config_path, config, agent_id, desired_workspace)
171
+ if resolved_openclaw_id:
172
+ target_agent_id = resolved_openclaw_id
173
+ elif resolved_previous is not None:
174
+ target_agent_id = resolved_previous.get("id") or agent_id
175
175
  else:
176
- target_agent_id = resolved_openclaw_id or agent_id
176
+ target_agent_id = agent_id
177
+ # previous = 目标 id 对应的已有条目(更新场景);校验若已被不同源 agent 占用则报错
178
+ previous = find_agent_entry(config, target_agent_id)
179
+ if previous is not None and not entry_matches_source_agent(config_path, previous, agent_id):
180
+ raise ValueError(f"目标 openclaw id 已存在且不属于当前 Agent: {target_agent_id}")
177
181
 
178
182
  entry = build_agent_entry(
179
183
  agent_def,