bookmark-research 0.5.0-beta.18 → 0.5.0-beta.19

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.
@@ -9,7 +9,7 @@
9
9
  "source": {
10
10
  "source": "npm",
11
11
  "package": "bookmark-research",
12
- "version": "0.5.0-beta.18",
12
+ "version": "0.5.0-beta.19",
13
13
  "registry": "https://registry.npmjs.org"
14
14
  },
15
15
  "policy": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bookmark-research",
3
- "version": "0.5.0-beta.18",
3
+ "version": "0.5.0-beta.19",
4
4
  "description": "Check bookmark URLs or Canvas packages, compare sources with host-led research, and retain verifiable reports.",
5
5
  "author": {
6
6
  "name": "kwenxu"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bookmark-research",
3
- "version": "0.5.0-beta.18",
3
+ "version": "0.5.0-beta.19",
4
4
  "description": "Check bookmark URLs or Canvas packages, compare sources with host-led research, and retain verifiable reports.",
5
5
  "license": "GPL-3.0-only",
6
6
  "repository": {
package/src/onboarding.py CHANGED
@@ -119,57 +119,72 @@ class Console(Prompter):
119
119
  client = resolve_dsh(getattr(args, "dsh", None))
120
120
  if client["source"] == "desktop":
121
121
  args.dsh = client["binary"]
122
- if requested:
123
- hosts = list(dict.fromkeys(requested))
124
- else:
125
- found = {name: bool(shutil.which(getattr(args, name, None) or name)) for name, _, _ in HOSTS}
126
- hints = {name: ("", None) if found[name] else ("CLI not found", "未找到 CLI") for name in found}
127
- found["dsh"], hints["dsh"] = client["usable"], _dsh_hint(client)
128
- if not any(found.values()):
129
- self.warn("No supported host CLI (codex, claude, dsh) was found on PATH.",
130
- "PATH 中未找到任何受支持的宿主 CLI(codex、claude、dsh)。")
131
- raise ValueError("No host CLI found; install Codex, Claude Code or DeepSeek Harness (dsh), "
132
- "or pass --codex/--claude/--dsh PATH to point at its executable")
133
- hosts = self.multiselect("Install into which hosts?", "安装到哪些宿主?", [
134
- Option(name, en, zh, *hints[name], disabled=not found[name]) for name, en, zh in HOSTS],
135
- [name for name, _, _ in HOSTS if found[name]])
136
- # A carrier that ships its own pnpm is the Desktop CLI, however it was found:
137
- # only that CLI may manage the profile name Desktop reserves for itself.
138
- desktop = client["bundled_pnpm"] is not None
139
- targets = []
140
- for host in hosts:
141
- label = next((en for name, en, _ in HOSTS if name == host), host)
142
- target = {"host": host, "scope": None, "project": None, "profile": None}
143
- if host == "claude":
144
- scope = args.scope
145
- if scope is None:
146
- options = [Option("user", "This user", "当前用户"), Option("project", "This project", "指定项目")]
147
- if host == "claude":
148
- options.append(Option("local", "This project, only for me", "指定项目(仅自己)"))
149
- scope = self.select(label + ": installation scope", label + ":安装范围", options, "user")
150
- target["scope"] = scope
151
- if scope in ("project", "local"):
152
- target["project"] = args.project or self.input(
153
- label + ": project directory", label + ":项目目录", str(Path.cwd()), _directory_problem)
154
- elif host == "dsh":
155
- # Never guess a DSH profile: it selects a distinct composed runtime.
156
- target["profile"] = args.profile or self.dsh_profile(desktop)
157
- targets.append(target)
158
- lines = []
159
- for target in targets:
160
- label = next((en for name, en, _ in HOSTS if name == target["host"]), target["host"])
161
- detail = ", ".join(key + "=" + target[key] for key in ("scope", "project", "profile") if target[key])
162
- lines.append((label + (": " + detail if detail else ""), label + (":" + detail if detail else "")))
163
- self.note("Installation plan", "安装计划", lines)
164
- if client["source"] == "desktop" and any(target["host"] == "dsh" for target in targets):
165
- self.note("DeepSeek Harness (dsh) CLI", "DeepSeek Harness (dsh) CLI", [(client["binary"], client["binary"])])
166
- if not self.confirm("Install now?", "现在安装?", True):
167
- raise KeyboardInterrupt
168
- first = targets[0]
169
- args.host, args.scope, args.project, args.profile = first["host"], first["scope"], first["project"], first["profile"]
170
- return targets
171
-
172
- def dsh_profile(self, desktop):
122
+ while True:
123
+ if requested:
124
+ hosts = list(dict.fromkeys(requested))
125
+ else:
126
+ found = {name: bool(shutil.which(getattr(args, name, None) or name)) for name, _, _ in HOSTS}
127
+ hints = {name: ("", None) if found[name] else ("CLI not found", "未找到 CLI") for name in found}
128
+ found["dsh"], hints["dsh"] = client["usable"], _dsh_hint(client)
129
+ if not any(found.values()):
130
+ self.warn("No supported host CLI (codex, claude, dsh) was found on PATH.",
131
+ "PATH 中未找到任何受支持的宿主 CLI(codex、claude、dsh)。")
132
+ raise ValueError("No host CLI found; install Codex, Claude Code or DeepSeek Harness (dsh), "
133
+ "or pass --codex/--claude/--dsh PATH to point at its executable")
134
+ hosts = self.multiselect("Install into which hosts? (Space to toggle, Enter to confirm)",
135
+ "安装到哪些宿主?(空格 勾选/取消,回车 确认)", [
136
+ Option(name, en, zh, *hints[name], disabled=not found[name]) for name, en, zh in HOSTS],
137
+ [name for name, _, _ in HOSTS if found[name]])
138
+ # A carrier that ships its own pnpm is the Desktop CLI, however it was found:
139
+ # only that CLI may manage the profile name Desktop reserves for itself.
140
+ desktop = client["bundled_pnpm"] is not None
141
+ targets = []
142
+ go_back = False
143
+ for host in hosts:
144
+ label = next((en for name, en, _ in HOSTS if name == host), host)
145
+ target = {"host": host, "scope": None, "project": None, "profile": None}
146
+ if host == "claude":
147
+ scope = args.scope
148
+ if scope is None:
149
+ options = [Option("user", "This user", "当前用户"), Option("project", "This project", "指定项目"),
150
+ Option("local", "This project, only for me", "指定项目(仅自己)")]
151
+ if not requested:
152
+ options.append(Option("back", "← Back to host selection", "← 返回重新选择宿主"))
153
+ scope = self.select(label + ": installation scope", label + ":安装范围", options, "user")
154
+ if scope == "back":
155
+ go_back = True
156
+ break
157
+ target["scope"] = scope
158
+ if scope in ("project", "local"):
159
+ target["project"] = args.project or self.input(
160
+ label + ": project directory", label + ":项目目录", str(Path.cwd()), _directory_problem)
161
+ elif host == "dsh":
162
+ # Never guess a DSH profile: it selects a distinct composed runtime.
163
+ profile = args.profile or self.dsh_profile(desktop, allow_back=not requested)
164
+ if profile == "back":
165
+ go_back = True
166
+ break
167
+ target["profile"] = profile
168
+ targets.append(target)
169
+ if go_back:
170
+ continue
171
+ lines = []
172
+ for target in targets:
173
+ label = next((en for name, en, _ in HOSTS if name == target["host"]), target["host"])
174
+ detail = ", ".join(key + "=" + target[key] for key in ("scope", "project", "profile") if target[key])
175
+ lines.append((label + (": " + detail if detail else ""), label + (":" + detail if detail else "")))
176
+ self.note("Installation plan", "安装计划", lines)
177
+ if client["source"] == "desktop" and any(target["host"] == "dsh" for target in targets):
178
+ self.note("DeepSeek Harness (dsh) CLI", "DeepSeek Harness (dsh) CLI", [(client["binary"], client["binary"])])
179
+ if not self.confirm("Install now?", "现在安装?", True):
180
+ if not requested and self.confirm("Re-select hosts and options?", "重新选择宿主与选项?", True):
181
+ continue
182
+ raise KeyboardInterrupt
183
+ first = targets[0]
184
+ args.host, args.scope, args.project, args.profile = first["host"], first["scope"], first["project"], first["profile"]
185
+ return targets
186
+
187
+ def dsh_profile(self, desktop, allow_back=False):
173
188
  """Choose an initialized DSH profile, or name a new one.
174
189
 
175
190
  `dsh plugin` initializes a profile on first use, so the profiles on disk
@@ -186,8 +201,12 @@ class Console(Prompter):
186
201
  return self.input(question[0], question[1], "desktop" if desktop else "", check)
187
202
  options = [Option(row["name"], row["name"], row["name"], *_profile_hint(row)) for row in rows]
188
203
  options.append(Option("", "Another profile name…", "其他 profile 名称…"))
204
+ if allow_back:
205
+ options.append(Option("back", "← Back to host selection", "← 返回重新选择宿主"))
189
206
  default = "desktop" if any(row["name"] == "desktop" for row in rows) else None
190
207
  chosen = self.select("Install into which DeepSeek Harness (dsh) profile?", "安装到哪个 DeepSeek Harness (dsh) profile?", options, default)
208
+ if chosen == "back":
209
+ return "back"
191
210
  if chosen:
192
211
  return chosen
193
212
  return self.input(question[0], question[1], "desktop" if desktop else "", check)
package/src/tui.py CHANGED
@@ -449,8 +449,11 @@ class Prompter:
449
449
  while True:
450
450
  lines = self._render_list(question, options, cursor, chosen, True, message)
451
451
  if not message:
452
- lines[-1] = "%s %s" % (self._color("36", "└"), self._color("2", self.text(
453
- "space toggle · a all · enter confirm", "空格 切换 · a 全选 · 回车 确认")))
452
+ shortcut_hint = self.text(
453
+ "[Space] toggle [a] all/none [Enter] confirm",
454
+ "[空格] 勾选/取消 [a] 全选/全不选 [回车] 确认",
455
+ )
456
+ lines[-1] = "%s %s" % (self._color("36", "└"), self._color("36", shortcut_hint))
454
457
  drawn = self._redraw(lines, drawn)
455
458
  key = self._key()
456
459
  message = None