beads-ui 0.4.2 → 0.4.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "beads-ui",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
4
  "description": "Local UI for Beads — Collaborate on issues with your coding agent.",
5
5
  "keywords": [
6
6
  "agent",
@@ -9,6 +9,7 @@
9
9
  "ai-tools"
10
10
  ],
11
11
  "homepage": "https://github.com/mantoni/beads-ui",
12
+ "author": "Maximilian Antoni <mail@maxantoni.de>",
12
13
  "type": "module",
13
14
  "bin": {
14
15
  "bdui": "bin/bdui.js"
@@ -57,7 +58,7 @@
57
58
  "jsdom": "^27.0.1",
58
59
  "prettier": "^3.3.3",
59
60
  "typescript": "^5.6.3",
60
- "vitest": "^2.1.3"
61
+ "vitest": "^4.0.6"
61
62
  },
62
63
  "files": [
63
64
  "app/index.html",
package/server/bd.js CHANGED
@@ -1,5 +1,8 @@
1
1
  import { spawn } from 'node:child_process';
2
2
  import { resolveDbPath } from './db.js';
3
+ import { debug } from './logging.js';
4
+
5
+ const log = debug('bd');
3
6
 
4
7
  /**
5
8
  * Resolve the bd executable path.
@@ -24,15 +27,25 @@ export function getBdBin() {
24
27
  */
25
28
  export function runBd(args, options = {}) {
26
29
  const bin = getBdBin();
30
+
31
+ // Ensure a consistent DB by setting BEADS_DB environment variable
32
+ const db_path = resolveDbPath({
33
+ cwd: options.cwd || process.cwd(),
34
+ env: options.env || process.env
35
+ });
36
+ const env_with_db = {
37
+ ...(options.env || process.env),
38
+ BEADS_DB: db_path.path
39
+ };
40
+
27
41
  const spawn_opts = {
28
42
  cwd: options.cwd || process.cwd(),
29
- env: options.env ? options.env : process.env,
43
+ env: env_with_db,
30
44
  shell: false
31
45
  };
32
46
 
33
- // Ensure a consistent DB by injecting --db if missing, following beads precedence.
34
47
  /** @type {string[]} */
35
- const final_args = withDbArg(args, spawn_opts.cwd, spawn_opts.env);
48
+ const final_args = args.slice();
36
49
 
37
50
  return new Promise((resolve) => {
38
51
  const child = spawn(bin, final_args, spawn_opts);
@@ -78,8 +91,9 @@ export function runBd(args, options = {}) {
78
91
  });
79
92
  };
80
93
 
81
- child.on('error', () => {
82
- // Treat spawn error as an immediate non-zero exit with captured stderr message.
94
+ child.on('error', (err) => {
95
+ // Treat spawn error as an immediate non-zero exit; log for diagnostics.
96
+ log('spawn error running %s %o', bin, err);
83
97
  finish(127);
84
98
  });
85
99
  child.on('close', (code) => {
@@ -98,30 +112,21 @@ export function runBd(args, options = {}) {
98
112
  export async function runBdJson(args, options = {}) {
99
113
  const result = await runBd(args, options);
100
114
  if (result.code !== 0) {
115
+ log(
116
+ 'bd exited with code %d (args=%o) stderr=%s',
117
+ result.code,
118
+ args,
119
+ result.stderr
120
+ );
101
121
  return { code: result.code, stderr: result.stderr };
102
122
  }
103
123
  /** @type {unknown} */
104
124
  let parsed;
105
125
  try {
106
126
  parsed = JSON.parse(result.stdout || 'null');
107
- } catch {
127
+ } catch (err) {
128
+ log('bd returned invalid JSON (args=%o): %o', args, err);
108
129
  return { code: 0, stderr: 'Invalid JSON from bd' };
109
130
  }
110
131
  return { code: 0, stdoutJson: parsed };
111
132
  }
112
-
113
- /**
114
- * Add a resolved "--db <path>" pair to args when none present.
115
- *
116
- * @param {string[]} args
117
- * @param {string} cwd
118
- * @param {Record<string, string | undefined>} env
119
- * @returns {string[]}
120
- */
121
- function withDbArg(args, cwd, env) {
122
- if (args.includes('--db')) {
123
- return args.slice();
124
- }
125
- const resolved = resolveDbPath({ cwd, env });
126
- return ['--db', resolved.path, ...args];
127
- }
@@ -1,4 +1,7 @@
1
1
  import { runBdJson } from './bd.js';
2
+ import { debug } from './logging.js';
3
+
4
+ const log = debug('list-adapters');
2
5
 
3
6
  /**
4
7
  * Build concrete `bd` CLI args for a subscription type + params.
@@ -107,6 +110,8 @@ export async function fetchListForSubscription(spec) {
107
110
  try {
108
111
  args = mapSubscriptionToBdArgs(spec);
109
112
  } catch (err) {
113
+ // Surface bad requests (e.g., missing params)
114
+ log('mapSubscriptionToBdArgs failed for %o: %o', spec, err);
110
115
  const e = toErrorObject(err);
111
116
  return { ok: false, error: e };
112
117
  }
@@ -114,6 +119,13 @@ export async function fetchListForSubscription(spec) {
114
119
  try {
115
120
  const res = await runBdJson(args);
116
121
  if (!res || res.code !== 0 || !('stdoutJson' in res)) {
122
+ log(
123
+ 'bd failed for %o (args=%o) code=%s stderr=%s',
124
+ spec,
125
+ args,
126
+ res?.code,
127
+ res?.stderr || ''
128
+ );
117
129
  return {
118
130
  ok: false,
119
131
  error: {
@@ -161,6 +173,7 @@ export async function fetchListForSubscription(spec) {
161
173
  const items = normalizeIssueList(raw);
162
174
  return { ok: true, items };
163
175
  } catch (err) {
176
+ log('bd invocation failed for %o (args=%o): %o', spec, args, err);
164
177
  return {
165
178
  ok: false,
166
179
  error: {
package/server/ws.js CHANGED
@@ -250,8 +250,8 @@ function emitSubscriptionSnapshot(ws, client_id, key, issues) {
250
250
  });
251
251
  try {
252
252
  ws.send(msg);
253
- } catch {
254
- // ignore per-socket errors
253
+ } catch (err) {
254
+ log('emit snapshot send failed key=%s id=%s: %o', key, client_id, err);
255
255
  }
256
256
  }
257
257
 
@@ -277,8 +277,8 @@ function emitSubscriptionUpsert(ws, client_id, key, issue) {
277
277
  });
278
278
  try {
279
279
  ws.send(msg);
280
- } catch {
281
- // ignore
280
+ } catch (err) {
281
+ log('emit upsert send failed key=%s id=%s: %o', key, client_id, err);
282
282
  }
283
283
  }
284
284
 
@@ -304,8 +304,8 @@ function emitSubscriptionDelete(ws, client_id, key, issue_id) {
304
304
  });
305
305
  try {
306
306
  ws.send(msg);
307
- } catch {
308
- // ignore
307
+ } catch (err) {
308
+ log('emit delete send failed key=%s id=%s: %o', key, client_id, err);
309
309
  }
310
310
  }
311
311
 
@@ -322,6 +322,7 @@ async function refreshAndPublish(spec) {
322
322
  await registry.withKeyLock(key, async () => {
323
323
  const res = await fetchListForSubscription(spec);
324
324
  if (!res.ok) {
325
+ log('refresh failed for %s: %s %o', key, res.error.message, res.error);
325
326
  return;
326
327
  }
327
328
  const items = applyClosedIssuesFilter(spec, res.items);
@@ -554,14 +555,20 @@ export async function handleMessage(ws, data) {
554
555
  await registry.withKeyLock(key, async () => {
555
556
  const res = await fetchListForSubscription(spec);
556
557
  if (!res.ok) {
558
+ log(
559
+ 'initial snapshot failed for %s: %s %o',
560
+ key,
561
+ res.error.message,
562
+ res.error
563
+ );
557
564
  return;
558
565
  }
559
566
  const items = applyClosedIssuesFilter(spec, res.items);
560
567
  void registry.applyItems(key, items);
561
568
  emitSubscriptionSnapshot(ws, client_id, key, items);
562
569
  });
563
- } catch {
564
- // ignore snapshot errors
570
+ } catch (err) {
571
+ log('subscribe-list snapshot error for %s: %o', key, err);
565
572
  }
566
573
  ws.send(JSON.stringify(makeOk(req, { id: client_id, key })));
567
574
  return;