beads-ui 0.9.2 → 0.10.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/CHANGES.md +24 -0
- package/README.md +2 -0
- package/app/styles.css +1 -0
- package/package.json +1 -1
- package/server/bd.js +45 -5
- package/server/cli/commands.js +7 -4
- package/server/cli/daemon.js +3 -2
- package/server/cli/index.js +26 -0
- package/server/cli/usage.js +1 -0
- package/server/db.js +55 -3
- package/server/index.js +7 -4
- package/server/list-adapters.js +1 -1
- package/server/watcher.js +29 -9
- package/server/ws.js +4 -4
package/CHANGES.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# Changes
|
|
2
2
|
|
|
3
|
+
## 0.10.0
|
|
4
|
+
|
|
5
|
+
- [`998f256`](https://github.com/mantoni/beads-ui/commit/998f2562b3ad3203c9dd1f627d44b1c2d5ef03a4)
|
|
6
|
+
Do not wrap issue IDs
|
|
7
|
+
- [`e3c3345`](https://github.com/mantoni/beads-ui/commit/e3c3345db41cd874db8e33ec79c904cc314e6bf8)
|
|
8
|
+
Improve workspace resolution and fallback db
|
|
9
|
+
- [`6de4652`](https://github.com/mantoni/beads-ui/commit/6de4652c336f77c8d8ec9cc13f5a47e9ba1b3857)
|
|
10
|
+
Avoid concurrent DB access to work around dolt panic
|
|
11
|
+
- [`011fe9e`](https://github.com/mantoni/beads-ui/commit/011fe9e3dfaa475f744b69ff6b44c3cc23283ad1)
|
|
12
|
+
Support dolt backend
|
|
13
|
+
- [`63ed3c3`](https://github.com/mantoni/beads-ui/commit/63ed3c3f3f98aa2c6d621537887d98701289dac6)
|
|
14
|
+
Update beads
|
|
15
|
+
- [`cd0a4c5`](https://github.com/mantoni/beads-ui/commit/cd0a4c59fcfe2c9a655ed2079a2a059a242906c5)
|
|
16
|
+
docs: highlight multi-workspace feature in README (#47) (Pablo LION)
|
|
17
|
+
|
|
18
|
+
_Released by [Maximilian Antoni](https://github.com/mantoni) on 2026-02-25._
|
|
19
|
+
|
|
20
|
+
## 0.9.3
|
|
21
|
+
|
|
22
|
+
- [`2e04bc1`](https://github.com/mantoni/beads-ui/commit/2e04bc1eeb5c43e6934d858cd017d80f745a38bb)
|
|
23
|
+
Add -v/—version flag to CLI (#46) (Brent Traut)
|
|
24
|
+
|
|
25
|
+
_Released by [Maximilian Antoni](https://github.com/mantoni) on 2026-01-23._
|
|
26
|
+
|
|
3
27
|
## 0.9.2
|
|
4
28
|
|
|
5
29
|
- [`ffa376c`](https://github.com/mantoni/beads-ui/commit/ffa376cab432b0e321232e8bc0de2caca20a6b17)
|
package/README.md
CHANGED
|
@@ -22,6 +22,8 @@
|
|
|
22
22
|
- ⛰️ **Epics view** – Show progress per epic, expand rows, edit inline
|
|
23
23
|
- 🏂 **Board view** – Blocked / Ready / In progress / Closed columns
|
|
24
24
|
- ⌨️ **Keyboard navigation** – Navigate and edit without touching the mouse
|
|
25
|
+
- 🔀 **Multi-workspace** – Switch between projects via dropdown, auto-registers
|
|
26
|
+
workspaces
|
|
25
27
|
|
|
26
28
|
## Setup
|
|
27
29
|
|
package/app/styles.css
CHANGED
package/package.json
CHANGED
package/server/bd.js
CHANGED
|
@@ -3,6 +3,8 @@ import { resolveDbPath } from './db.js';
|
|
|
3
3
|
import { debug } from './logging.js';
|
|
4
4
|
|
|
5
5
|
const log = debug('bd');
|
|
6
|
+
/** @type {Promise<void>} */
|
|
7
|
+
let bd_run_queue = Promise.resolve();
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
* Get the git user name from git config.
|
|
@@ -59,17 +61,30 @@ export function getBdBin() {
|
|
|
59
61
|
* @returns {Promise<{ code: number, stdout: string, stderr: string }>}
|
|
60
62
|
*/
|
|
61
63
|
export function runBd(args, options = {}) {
|
|
64
|
+
return withBdRunQueue(async () => runBdUnlocked(args, options));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Run the `bd` CLI with provided arguments without queueing.
|
|
69
|
+
*
|
|
70
|
+
* @param {string[]} args
|
|
71
|
+
* @param {{ cwd?: string, env?: Record<string, string | undefined>, timeout_ms?: number }} [options]
|
|
72
|
+
* @returns {Promise<{ code: number, stdout: string, stderr: string }>}
|
|
73
|
+
*/
|
|
74
|
+
function runBdUnlocked(args, options = {}) {
|
|
62
75
|
const bin = getBdBin();
|
|
63
76
|
|
|
64
|
-
//
|
|
77
|
+
// Set BEADS_DB only when the workspace has a local SQLite DB.
|
|
78
|
+
// Do not force BEADS_DB from global fallback paths; this can override
|
|
79
|
+
// backend autodetection in non-SQLite workspaces (for example Dolt).
|
|
65
80
|
const db_path = resolveDbPath({
|
|
66
81
|
cwd: options.cwd || process.cwd(),
|
|
67
82
|
env: options.env || process.env
|
|
68
83
|
});
|
|
69
|
-
const env_with_db = {
|
|
70
|
-
|
|
71
|
-
BEADS_DB
|
|
72
|
-
}
|
|
84
|
+
const env_with_db = { ...(options.env || process.env) };
|
|
85
|
+
if (db_path.source === 'nearest' && db_path.exists) {
|
|
86
|
+
env_with_db.BEADS_DB = db_path.path;
|
|
87
|
+
}
|
|
73
88
|
|
|
74
89
|
const spawn_opts = {
|
|
75
90
|
cwd: options.cwd || process.cwd(),
|
|
@@ -136,6 +151,31 @@ export function runBd(args, options = {}) {
|
|
|
136
151
|
});
|
|
137
152
|
}
|
|
138
153
|
|
|
154
|
+
/**
|
|
155
|
+
* Serialize `bd` invocations.
|
|
156
|
+
* Dolt embedded mode can crash when multiple `bd` processes run concurrently
|
|
157
|
+
* against the same workspace.
|
|
158
|
+
*
|
|
159
|
+
* @template T
|
|
160
|
+
* @param {() => Promise<T>} operation
|
|
161
|
+
* @returns {Promise<T>}
|
|
162
|
+
*/
|
|
163
|
+
async function withBdRunQueue(operation) {
|
|
164
|
+
const previous = bd_run_queue;
|
|
165
|
+
/** @type {() => void} */
|
|
166
|
+
let release = () => {};
|
|
167
|
+
bd_run_queue = new Promise((resolve) => {
|
|
168
|
+
release = resolve;
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
await previous.catch(() => {});
|
|
172
|
+
try {
|
|
173
|
+
return await operation();
|
|
174
|
+
} finally {
|
|
175
|
+
release();
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
139
179
|
/**
|
|
140
180
|
* Run `bd` and parse JSON from stdout if exit code is 0.
|
|
141
181
|
*
|
package/server/cli/commands.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getConfig } from '../config.js';
|
|
2
|
-
import {
|
|
2
|
+
import { resolveWorkspaceDatabase } from '../db.js';
|
|
3
3
|
import {
|
|
4
4
|
isProcessRunning,
|
|
5
5
|
printServerUrl,
|
|
@@ -25,12 +25,15 @@ export async function handleStart(options) {
|
|
|
25
25
|
if (existing_pid && isProcessRunning(existing_pid)) {
|
|
26
26
|
// Server is already running - register this workspace dynamically
|
|
27
27
|
const cwd = process.cwd();
|
|
28
|
-
const
|
|
29
|
-
if (
|
|
28
|
+
const workspace_database = resolveWorkspaceDatabase({ cwd });
|
|
29
|
+
if (
|
|
30
|
+
workspace_database.source !== 'home-default' &&
|
|
31
|
+
workspace_database.exists
|
|
32
|
+
) {
|
|
30
33
|
const { url } = getConfig();
|
|
31
34
|
const registered = await registerWorkspaceWithServer(url, {
|
|
32
35
|
path: cwd,
|
|
33
|
-
database:
|
|
36
|
+
database: workspace_database.path
|
|
34
37
|
});
|
|
35
38
|
if (registered) {
|
|
36
39
|
console.log('Workspace registered: %s', cwd);
|
package/server/cli/daemon.js
CHANGED
|
@@ -7,7 +7,7 @@ import os from 'node:os';
|
|
|
7
7
|
import path from 'node:path';
|
|
8
8
|
import { fileURLToPath } from 'node:url';
|
|
9
9
|
import { getConfig } from '../config.js';
|
|
10
|
-
import {
|
|
10
|
+
import { resolveWorkspaceDatabase } from '../db.js';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Resolve the runtime directory used for PID and log files.
|
|
@@ -171,6 +171,7 @@ export function startDaemon(options = {}) {
|
|
|
171
171
|
|
|
172
172
|
/** @type {SpawnOptions} */
|
|
173
173
|
const opts = {
|
|
174
|
+
cwd: process.cwd(),
|
|
174
175
|
detached: true,
|
|
175
176
|
env: spawn_env,
|
|
176
177
|
stdio: log_fd >= 0 ? ['ignore', log_fd, log_fd] : 'ignore',
|
|
@@ -260,7 +261,7 @@ function sleep(ms) {
|
|
|
260
261
|
*/
|
|
261
262
|
export function printServerUrl() {
|
|
262
263
|
// Resolve from the caller's working directory by default
|
|
263
|
-
const resolved_db =
|
|
264
|
+
const resolved_db = resolveWorkspaceDatabase();
|
|
264
265
|
console.log(
|
|
265
266
|
`beads db ${resolved_db.path} (${resolved_db.source}${resolved_db.exists ? '' : ', missing'})`
|
|
266
267
|
);
|
package/server/cli/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
1
2
|
import { enableAllDebug } from '../logging.js';
|
|
2
3
|
import { handleRestart, handleStart, handleStop } from './commands.js';
|
|
3
4
|
import { printUsage } from './usage.js';
|
|
@@ -30,6 +31,10 @@ export function parseArgs(args) {
|
|
|
30
31
|
flags.push('open');
|
|
31
32
|
continue;
|
|
32
33
|
}
|
|
34
|
+
if (token === '--version' || token === '-v') {
|
|
35
|
+
flags.push('version');
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
33
38
|
if (token === '--host' && i + 1 < args.length) {
|
|
34
39
|
options.host = args[++i];
|
|
35
40
|
continue;
|
|
@@ -54,6 +59,22 @@ export function parseArgs(args) {
|
|
|
54
59
|
return { command, flags, options };
|
|
55
60
|
}
|
|
56
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Load the package.json version string.
|
|
64
|
+
*
|
|
65
|
+
* @returns {Promise<string>}
|
|
66
|
+
*/
|
|
67
|
+
async function loadVersion() {
|
|
68
|
+
const package_url = new URL('../../package.json', import.meta.url);
|
|
69
|
+
const package_text = await readFile(package_url, 'utf8');
|
|
70
|
+
const package_data = JSON.parse(package_text);
|
|
71
|
+
const version = package_data.version;
|
|
72
|
+
if (typeof version !== 'string') {
|
|
73
|
+
throw new Error('Invalid package.json version');
|
|
74
|
+
}
|
|
75
|
+
return version;
|
|
76
|
+
}
|
|
77
|
+
|
|
57
78
|
/**
|
|
58
79
|
* CLI main entry. Returns an exit code and prints usage on `--help` or errors.
|
|
59
80
|
* No side effects beyond invoking stub handlers.
|
|
@@ -69,6 +90,11 @@ export async function main(args) {
|
|
|
69
90
|
enableAllDebug();
|
|
70
91
|
}
|
|
71
92
|
|
|
93
|
+
if (flags.includes('version')) {
|
|
94
|
+
const version = await loadVersion();
|
|
95
|
+
process.stdout.write(`${version}\n`);
|
|
96
|
+
return 0;
|
|
97
|
+
}
|
|
72
98
|
if (flags.includes('help')) {
|
|
73
99
|
printUsage(process.stdout);
|
|
74
100
|
return 0;
|
package/server/cli/usage.js
CHANGED
|
@@ -14,6 +14,7 @@ export function printUsage(out_stream) {
|
|
|
14
14
|
'',
|
|
15
15
|
'Options:',
|
|
16
16
|
' -h, --help Show this help message',
|
|
17
|
+
' -v, --version Show the CLI version',
|
|
17
18
|
' -d, --debug Enable debug logging',
|
|
18
19
|
' --open Open the browser after start/restart',
|
|
19
20
|
' --host <addr> Bind to a specific host (default: 127.0.0.1)',
|
package/server/db.js
CHANGED
|
@@ -6,7 +6,8 @@ import path from 'node:path';
|
|
|
6
6
|
* Resolve the SQLite DB path used by beads according to precedence:
|
|
7
7
|
* 1) explicit --db flag (provided via options.explicit_db)
|
|
8
8
|
* 2) BEADS_DB environment variable
|
|
9
|
-
* 3) nearest ".beads/*.db" by walking up from cwd
|
|
9
|
+
* 3) nearest ".beads/*.db" by walking up from cwd (excluding
|
|
10
|
+
* "~/.beads/default.db", which is reserved for fallback)
|
|
10
11
|
* 4) "~/.beads/default.db" fallback
|
|
11
12
|
*
|
|
12
13
|
* Returns a normalized absolute path and a `source` indicator. Existence is
|
|
@@ -18,6 +19,7 @@ import path from 'node:path';
|
|
|
18
19
|
export function resolveDbPath(options = {}) {
|
|
19
20
|
const cwd = options.cwd ? path.resolve(options.cwd) : process.cwd();
|
|
20
21
|
const env = options.env || process.env;
|
|
22
|
+
const home_default = path.join(os.homedir(), '.beads', 'default.db');
|
|
21
23
|
|
|
22
24
|
// 1) explicit flag
|
|
23
25
|
if (options.explicit_db && options.explicit_db.length > 0) {
|
|
@@ -33,12 +35,11 @@ export function resolveDbPath(options = {}) {
|
|
|
33
35
|
|
|
34
36
|
// 3) nearest .beads/*.db walking up
|
|
35
37
|
const nearest = findNearestBeadsDb(cwd);
|
|
36
|
-
if (nearest) {
|
|
38
|
+
if (nearest && path.normalize(nearest) !== path.normalize(home_default)) {
|
|
37
39
|
return { path: nearest, source: 'nearest', exists: fileExists(nearest) };
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
// 4) ~/.beads/default.db
|
|
41
|
-
const home_default = path.join(os.homedir(), '.beads', 'default.db');
|
|
42
43
|
return {
|
|
43
44
|
path: home_default,
|
|
44
45
|
source: 'home-default',
|
|
@@ -46,6 +47,57 @@ export function resolveDbPath(options = {}) {
|
|
|
46
47
|
};
|
|
47
48
|
}
|
|
48
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Resolve the workspace database location used by the UI/server.
|
|
52
|
+
*
|
|
53
|
+
* For non-SQLite backends (for example Dolt), this returns the nearest
|
|
54
|
+
* workspace `.beads` directory when metadata exists. This avoids collapsing
|
|
55
|
+
* all such workspaces onto the `~/.beads/default.db` fallback.
|
|
56
|
+
*
|
|
57
|
+
* @param {{ cwd?: string, env?: Record<string, string | undefined>, explicit_db?: string }} [options]
|
|
58
|
+
* @returns {{ path: string, source: 'flag'|'env'|'nearest'|'metadata'|'home-default', exists: boolean }}
|
|
59
|
+
*/
|
|
60
|
+
export function resolveWorkspaceDatabase(options = {}) {
|
|
61
|
+
const sqlite_db = resolveDbPath(options);
|
|
62
|
+
if (sqlite_db.source !== 'home-default') {
|
|
63
|
+
return sqlite_db;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const cwd = options.cwd ? path.resolve(options.cwd) : process.cwd();
|
|
67
|
+
const metadata_path = findNearestBeadsMetadata(cwd);
|
|
68
|
+
if (metadata_path) {
|
|
69
|
+
return {
|
|
70
|
+
path: path.dirname(metadata_path),
|
|
71
|
+
source: 'metadata',
|
|
72
|
+
exists: true
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return sqlite_db;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Find nearest `.beads/metadata.json` by walking up from start.
|
|
81
|
+
*
|
|
82
|
+
* @param {string} start
|
|
83
|
+
* @returns {string | null}
|
|
84
|
+
*/
|
|
85
|
+
export function findNearestBeadsMetadata(start) {
|
|
86
|
+
let dir = path.resolve(start);
|
|
87
|
+
for (let i = 0; i < 100; i++) {
|
|
88
|
+
const metadata_path = path.join(dir, '.beads', 'metadata.json');
|
|
89
|
+
if (fileExists(metadata_path)) {
|
|
90
|
+
return metadata_path;
|
|
91
|
+
}
|
|
92
|
+
const parent = path.dirname(dir);
|
|
93
|
+
if (parent === dir) {
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
dir = parent;
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
|
|
49
101
|
/**
|
|
50
102
|
* Find nearest .beads/*.db by walking up from start.
|
|
51
103
|
* First alphabetical .db.
|
package/server/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { createServer } from 'node:http';
|
|
|
2
2
|
import { createApp } from './app.js';
|
|
3
3
|
import { printServerUrl } from './cli/daemon.js';
|
|
4
4
|
import { getConfig } from './config.js';
|
|
5
|
-
import {
|
|
5
|
+
import { resolveWorkspaceDatabase } from './db.js';
|
|
6
6
|
import { debug, enableAllDebug } from './logging.js';
|
|
7
7
|
import { registerWorkspace, watchRegistry } from './registry-watcher.js';
|
|
8
8
|
import { watchDb } from './watcher.js';
|
|
@@ -29,9 +29,12 @@ const log = debug('server');
|
|
|
29
29
|
|
|
30
30
|
// Register the initial workspace (from cwd) so it appears in the workspace picker
|
|
31
31
|
// even without the beads daemon running
|
|
32
|
-
const
|
|
33
|
-
if (
|
|
34
|
-
registerWorkspace({
|
|
32
|
+
const workspace_database = resolveWorkspaceDatabase({ cwd: config.root_dir });
|
|
33
|
+
if (workspace_database.source !== 'home-default' && workspace_database.exists) {
|
|
34
|
+
registerWorkspace({
|
|
35
|
+
path: config.root_dir,
|
|
36
|
+
database: workspace_database.path
|
|
37
|
+
});
|
|
35
38
|
}
|
|
36
39
|
|
|
37
40
|
// Watch the active beads DB and schedule subscription refresh for active lists
|
package/server/list-adapters.js
CHANGED
|
@@ -29,7 +29,7 @@ export function mapSubscriptionToBdArgs(spec) {
|
|
|
29
29
|
return ['list', '--json', '--status', 'in_progress'];
|
|
30
30
|
}
|
|
31
31
|
case 'closed-issues': {
|
|
32
|
-
return ['list', '--json', '--status', 'closed'];
|
|
32
|
+
return ['list', '--json', '--status', 'closed', '--limit', '1000'];
|
|
33
33
|
}
|
|
34
34
|
case 'issue-detail': {
|
|
35
35
|
const p = spec.params || {};
|
package/server/watcher.js
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import {
|
|
3
|
+
import { resolveWorkspaceDatabase } from './db.js';
|
|
4
4
|
import { debug } from './logging.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* Watch the resolved
|
|
8
|
-
*
|
|
7
|
+
* Watch the resolved workspace database target and invoke a callback after a
|
|
8
|
+
* debounce window.
|
|
9
|
+
*
|
|
10
|
+
* For SQLite workspaces this watches the DB file's parent directory and filters
|
|
11
|
+
* by file name. For non-SQLite backends (for example Dolt), this watches the
|
|
12
|
+
* workspace `.beads` directory.
|
|
9
13
|
*
|
|
10
14
|
* @param {string} root_dir - Project root directory (starting point for resolution).
|
|
11
15
|
* @param {() => void} onChange - Called when changes are detected.
|
|
@@ -47,13 +51,18 @@ export function watchDb(root_dir, onChange, options = {}) {
|
|
|
47
51
|
* @param {string | undefined} explicit_db
|
|
48
52
|
*/
|
|
49
53
|
const bind = (base_dir, explicit_db) => {
|
|
50
|
-
const resolved =
|
|
54
|
+
const resolved = resolveWorkspaceDatabase({ cwd: base_dir, explicit_db });
|
|
51
55
|
current_path = resolved.path;
|
|
52
|
-
|
|
53
|
-
|
|
56
|
+
if (pathIsDirectory(current_path)) {
|
|
57
|
+
current_dir = current_path;
|
|
58
|
+
current_file = '';
|
|
59
|
+
} else {
|
|
60
|
+
current_dir = path.dirname(current_path);
|
|
61
|
+
current_file = path.basename(current_path);
|
|
62
|
+
}
|
|
54
63
|
if (!resolved.exists) {
|
|
55
64
|
log(
|
|
56
|
-
'resolved
|
|
65
|
+
'resolved workspace database missing: %s – Hint: set --db, export BEADS_DB, or run `bd init` in your workspace.',
|
|
57
66
|
current_path
|
|
58
67
|
);
|
|
59
68
|
}
|
|
@@ -64,7 +73,7 @@ export function watchDb(root_dir, onChange, options = {}) {
|
|
|
64
73
|
current_dir,
|
|
65
74
|
{ persistent: true },
|
|
66
75
|
(event_type, filename) => {
|
|
67
|
-
if (filename && String(filename) !== current_file) {
|
|
76
|
+
if (current_file && filename && String(filename) !== current_file) {
|
|
68
77
|
return;
|
|
69
78
|
}
|
|
70
79
|
if (event_type === 'change' || event_type === 'rename') {
|
|
@@ -103,7 +112,7 @@ export function watchDb(root_dir, onChange, options = {}) {
|
|
|
103
112
|
rebind(opts = {}) {
|
|
104
113
|
const next_root = opts.root_dir ? String(opts.root_dir) : root_dir;
|
|
105
114
|
const next_explicit = opts.explicit_db ?? options.explicit_db;
|
|
106
|
-
const next_resolved =
|
|
115
|
+
const next_resolved = resolveWorkspaceDatabase({
|
|
107
116
|
cwd: next_root,
|
|
108
117
|
explicit_db: next_explicit
|
|
109
118
|
});
|
|
@@ -117,3 +126,14 @@ export function watchDb(root_dir, onChange, options = {}) {
|
|
|
117
126
|
}
|
|
118
127
|
};
|
|
119
128
|
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* @param {string} file_path
|
|
132
|
+
*/
|
|
133
|
+
function pathIsDirectory(file_path) {
|
|
134
|
+
try {
|
|
135
|
+
return fs.statSync(file_path).isDirectory();
|
|
136
|
+
} catch {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}
|
package/server/ws.js
CHANGED
|
@@ -7,7 +7,7 @@ import path from 'node:path';
|
|
|
7
7
|
import { WebSocketServer } from 'ws';
|
|
8
8
|
import { isRequest, makeError, makeOk } from '../app/protocol.js';
|
|
9
9
|
import { getGitUserName, runBd, runBdJson } from './bd.js';
|
|
10
|
-
import {
|
|
10
|
+
import { resolveWorkspaceDatabase } from './db.js';
|
|
11
11
|
import { fetchListForSubscription } from './list-adapters.js';
|
|
12
12
|
import { debug } from './logging.js';
|
|
13
13
|
import { getAvailableWorkspaces } from './registry-watcher.js';
|
|
@@ -427,7 +427,7 @@ export function attachWsServer(http_server, options = {}) {
|
|
|
427
427
|
|
|
428
428
|
// Initialize workspace state
|
|
429
429
|
const initial_root = options.root_dir || process.cwd();
|
|
430
|
-
const initial_db =
|
|
430
|
+
const initial_db = resolveWorkspaceDatabase({ cwd: initial_root });
|
|
431
431
|
CURRENT_WORKSPACE = {
|
|
432
432
|
root_dir: initial_root,
|
|
433
433
|
db_path: initial_db.path
|
|
@@ -521,7 +521,7 @@ export function attachWsServer(http_server, options = {}) {
|
|
|
521
521
|
*/
|
|
522
522
|
function setWorkspace(new_root_dir) {
|
|
523
523
|
const resolved_root = path.resolve(new_root_dir);
|
|
524
|
-
const new_db =
|
|
524
|
+
const new_db = resolveWorkspaceDatabase({ cwd: resolved_root });
|
|
525
525
|
const old_path = CURRENT_WORKSPACE?.db_path || '';
|
|
526
526
|
|
|
527
527
|
CURRENT_WORKSPACE = {
|
|
@@ -1298,7 +1298,7 @@ export async function handleMessage(ws, data) {
|
|
|
1298
1298
|
const resolved = path.resolve(workspace_path);
|
|
1299
1299
|
|
|
1300
1300
|
// Update workspace (this will rebind watcher, clear registry, broadcast change)
|
|
1301
|
-
const new_db =
|
|
1301
|
+
const new_db = resolveWorkspaceDatabase({ cwd: resolved });
|
|
1302
1302
|
const old_path = CURRENT_WORKSPACE?.db_path || '';
|
|
1303
1303
|
|
|
1304
1304
|
CURRENT_WORKSPACE = {
|