@free-lzj/dsh-browser-use 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -41,6 +41,8 @@ bsk status # 要能看到 browsers connected 1
41
41
 
42
42
  插件不装扩展、不配浏览器;`bsk` 是它的运行时(见文末声明)。缺了 `bsk` 时插件照常加载,只是没有浏览器工具——`@` 提及和技能仍在。
43
43
 
44
+ **没装或连不上时,`@` 菜单不会空着**:它会显示一行「浏览器不可用」,后面跟 `bsk` 的原因和一句该做什么(`bsk --version` 确认装没装、`bsk status` 看 daemon 和浏览器连接)。插件不做任何下载或安装——`bsk` 不是独立分发的可执行文件,它是 BrowserSkill 那套东西的一部分,装法跟着它走。
45
+
44
46
  ## 安装
45
47
 
46
48
  ```sh
package/lib/client.js CHANGED
@@ -21,7 +21,8 @@ window.__ModuleLoader__.load({
21
21
  /**
22
22
  * The `@` menu's copy for this plugin.
23
23
  *
24
- * Only the section heading needs translating: every row's text is the user's own
24
+ * Only two things need translating: the section heading, and the row that
25
+ * explains why the section is missing. Every other row's text is the user's own
25
26
  * page title, which no dictionary can hold.
26
27
  *
27
28
  * @module @free-lzj/dsh-browser-use/client/locales
@@ -29,9 +30,15 @@ window.__ModuleLoader__.load({
29
30
  /** Dictionary namespace owned by this plugin. */
30
31
  const NS = "browserUse";
31
32
  /** Simplified Chinese dictionary (the key-set source of truth). */
32
- const zh = { sectionBrowser: "浏览器" };
33
+ const zh = {
34
+ sectionBrowser: "浏览器",
35
+ unavailable: "浏览器不可用"
36
+ };
33
37
  /** English dictionary, key-identical to the Chinese source of truth. */
34
- const en = { sectionBrowser: "Browser" };
38
+ const en = {
39
+ sectionBrowser: "Browser",
40
+ unavailable: "Browser unavailable"
41
+ };
35
42
  //#endregion
36
43
  //#region lib/types/client/index.js
37
44
  /**
@@ -82,7 +89,7 @@ window.__ModuleLoader__.load({
82
89
  * Ask the Host what can be mentioned.
83
90
  * @param query - text after `@`.
84
91
  * @param signal - the menu's cancellation.
85
- * @returns every reachable group; an unreachable Host yields no rows rather than an error.
92
+ * @returns every reachable group, plus the providers that could not answer.
86
93
  */
87
94
  async function fetchGroups(query, signal) {
88
95
  try {
@@ -90,14 +97,39 @@ window.__ModuleLoader__.load({
90
97
  signal,
91
98
  headers: { accept: "application/json" }
92
99
  });
93
- if (!response.ok) return [];
100
+ if (!response.ok) return {
101
+ groups: [],
102
+ failures: []
103
+ };
94
104
  const body = await response.json();
95
- return Array.isArray(body.groups) ? body.groups : [];
105
+ return {
106
+ groups: Array.isArray(body.groups) ? body.groups : [],
107
+ failures: Array.isArray(body.failures) ? body.failures : []
108
+ };
96
109
  } catch {
97
- return [];
110
+ return {
111
+ groups: [],
112
+ failures: []
113
+ };
98
114
  }
99
115
  }
100
116
  /**
117
+ * One non-selectable row explaining why the browser group is missing.
118
+ * @param failure - the Host's sentence, ready to read.
119
+ * @param t - the namespace's translator.
120
+ * @returns a single row for the menu.
121
+ */
122
+ function noticeRow(failure, t) {
123
+ return [{
124
+ name: "browser-use:unavailable",
125
+ label: t("unavailable"),
126
+ description: failure,
127
+ icon: "file",
128
+ section: t("sectionBrowser"),
129
+ value: JSON.stringify({ notice: true })
130
+ }];
131
+ }
132
+ /**
101
133
  * One candidate row per mentionable thing.
102
134
  * @param group - the provider's answer.
103
135
  * @param section - rendered heading.
@@ -141,13 +173,19 @@ window.__ModuleLoader__.load({
141
173
  showGroupTitle: false,
142
174
  async candidates(_session, req) {
143
175
  if (req.quoted === true) return [];
144
- const groups = await fetchGroups(req.query, req.signal);
176
+ const { groups, failures } = await fetchGroups(req.query, req.signal);
145
177
  if (req.signal.aborted) return [];
178
+ if (groups.length === 0) {
179
+ const first = failures[0];
180
+ return first === void 0 ? [] : noticeRow(first, t);
181
+ }
146
182
  return groups.flatMap((group) => rowsOf(group, sectionLabel(group, t)));
147
183
  },
148
184
  onPick({ candidate }) {
149
185
  const value = parseValue(candidate.value);
150
186
  if (value === void 0) return void 0;
187
+ if (value.notice === true) return void 0;
188
+ if (value.ref === void 0 || value.label === void 0) return void 0;
151
189
  const mention = `@[${value.label}](${mentionAddress(value.ref)})`;
152
190
  return { insert: {
153
191
  source: SOURCE_NAME,
@@ -167,12 +205,13 @@ window.__ModuleLoader__.load({
167
205
  /**
168
206
  * Read a row's pick payload.
169
207
  * @param value - the row's opaque `value`.
170
- * @returns the ref and label, or undefined when the row was not ours.
208
+ * @returns the payload, or undefined when the row was not ours.
171
209
  */
172
210
  function parseValue(value) {
173
211
  if (value === void 0) return void 0;
174
212
  try {
175
213
  const parsed = JSON.parse(value);
214
+ if (parsed.notice === true) return { notice: true };
176
215
  if (typeof parsed.ref !== "string" || typeof parsed.label !== "string") return void 0;
177
216
  return {
178
217
  ref: parsed.ref,
package/lib/index.js CHANGED
@@ -292,6 +292,45 @@ var BskMcpChannel = class {
292
292
  }
293
293
  };
294
294
  //#endregion
295
+ //#region lib/types/diagnose.js
296
+ /**
297
+ * Turning `bsk`'s own failure text into something a person can act on.
298
+ *
299
+ * The menu is the only place this appears, and it appears exactly when the
300
+ * browser group is missing — so the sentence has to answer "why is this empty"
301
+ * rather than describe an internal transport. `bsk` already writes a precise
302
+ * diagnosis; what it does not do is say what to do about it in this context.
303
+ *
304
+ * @module @free-lzj/dsh-browser-use/diagnose
305
+ */
306
+ /** Probes worth trying for any of these failures, cheapest first. */
307
+ const CHECK_COMMAND = "bsk --version";
308
+ const STATUS_COMMAND = "bsk status";
309
+ /**
310
+ * Explain one provider failure.
311
+ * @param message - the failure text, already prefixed with the provider id.
312
+ * @returns the cause and one next step.
313
+ */
314
+ function explainBsk(message) {
315
+ const text = message.toLowerCase();
316
+ if (text.includes("enoent") || text.includes("could not start") || text.includes("not found")) return {
317
+ reason: "找不到 BrowserSkill 的 bsk 命令(没装,或不在这个进程的 PATH 里)",
318
+ action: `在终端跑 ${CHECK_COMMAND} 确认;有多个 bsk 时用 DSH_BSK_PATH 指到对的那个`
319
+ };
320
+ if (text.includes("daemon") || text.includes("job object") || text.includes("breakaway")) return {
321
+ reason: "bsk 装好了,但它起不来或连不上后台 daemon",
322
+ action: `在终端跑 ${STATUS_COMMAND}:能连上说明是本进程受限,重启 dsh 桌面端;连不上就按 bsk 的提示处理`
323
+ };
324
+ if (text.includes("browser") && (text.includes("connect") || text.includes("session"))) return {
325
+ reason: "bsk 在跑,但没有浏览器连上来",
326
+ action: `打开浏览器并确认 BrowserSkill 扩展已启用,再用 ${STATUS_COMMAND} 看 browsers connected`
327
+ };
328
+ return {
329
+ reason: "BrowserSkill 没能列出标签页",
330
+ action: `在终端跑 ${STATUS_COMMAND} 看它的原话`
331
+ };
332
+ }
333
+ //#endregion
295
334
  //#region lib/types/browser-mention.js
296
335
  /**
297
336
  * The browser tab provider: the first, and for now the only, mentionable thing.
@@ -350,7 +389,13 @@ function browserMentionProvider(cli) {
350
389
  label: PROVIDER_LABEL,
351
390
  async candidates(query, signal) {
352
391
  const needle = query.trim().toLowerCase();
353
- const tabs = await cli.listUserTabs(signal);
392
+ let tabs;
393
+ try {
394
+ tabs = await cli.listUserTabs(signal);
395
+ } catch (error) {
396
+ const diagnosis = explainBsk(error instanceof Error ? error.message : String(error));
397
+ throw new Error(`${diagnosis.reason}。${diagnosis.action}`);
398
+ }
354
399
  if (signal.aborted) return [];
355
400
  return tabs.filter((tab) => matches(tab, needle)).map((tab) => ({
356
401
  ref: `${PROVIDER_ID}:${tab.tabId}`,
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * The `@` menu's copy for this plugin.
3
3
  *
4
- * Only the section heading needs translating: every row's text is the user's own
4
+ * Only two things need translating: the section heading, and the row that
5
+ * explains why the section is missing. Every other row's text is the user's own
5
6
  * page title, which no dictionary can hold.
6
7
  *
7
8
  * @module @free-lzj/dsh-browser-use/client/locales
@@ -11,6 +12,7 @@ export declare const NS = "browserUse";
11
12
  /** Simplified Chinese dictionary (the key-set source of truth). */
12
13
  export declare const zh: {
13
14
  readonly sectionBrowser: "浏览器";
15
+ readonly unavailable: "浏览器不可用";
14
16
  };
15
17
  /** English dictionary, key-identical to the Chinese source of truth. */
16
18
  export declare const en: Record<BrowserUseKey, string>;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Turning `bsk`'s own failure text into something a person can act on.
3
+ *
4
+ * The menu is the only place this appears, and it appears exactly when the
5
+ * browser group is missing — so the sentence has to answer "why is this empty"
6
+ * rather than describe an internal transport. `bsk` already writes a precise
7
+ * diagnosis; what it does not do is say what to do about it in this context.
8
+ *
9
+ * @module @free-lzj/dsh-browser-use/diagnose
10
+ */
11
+ /** One explanation: what went wrong, and the shortest thing that would fix it. */
12
+ export interface Diagnosis {
13
+ /** The cause, in the user's terms. */
14
+ readonly reason: string;
15
+ /** One concrete check or repair step. */
16
+ readonly action: string;
17
+ }
18
+ /**
19
+ * Explain one provider failure.
20
+ * @param message - the failure text, already prefixed with the provider id.
21
+ * @returns the cause and one next step.
22
+ */
23
+ export declare function explainBsk(message: string): Diagnosis;
24
+ //# sourceMappingURL=diagnose.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@free-lzj/dsh-browser-use",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Drive the browser you are already signed into: mention an open tab with @, then read and operate it through BrowserSkill's own tools",
5
5
  "repository": {
6
6
  "type": "git",