byterover-cli 2.3.0 → 2.3.1
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/dist/oclif/commands/restart.d.ts +34 -50
- package/dist/oclif/commands/restart.js +122 -209
- package/dist/oclif/hooks/init/block-command-update-npm.d.ts +11 -0
- package/dist/oclif/hooks/init/block-command-update-npm.js +15 -0
- package/dist/oclif/hooks/init/update-notifier.d.ts +3 -0
- package/dist/oclif/hooks/init/update-notifier.js +17 -4
- package/dist/oclif/hooks/postrun/restart-after-update.d.ts +22 -0
- package/dist/oclif/hooks/postrun/restart-after-update.js +40 -0
- package/dist/server/infra/http/openrouter-api-client.js +1 -1
- package/oclif.manifest.json +107 -100
- package/package.json +7 -5
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import { type EnsureDaemonResult } from '@campfirein/brv-transport-client';
|
|
2
1
|
import { Command } from '@oclif/core';
|
|
3
2
|
export default class Restart extends Command {
|
|
4
3
|
static description: string;
|
|
5
4
|
static examples: string[];
|
|
5
|
+
/** Commands whose processes must not be killed (e.g. `brv update` calls `brv restart`). */
|
|
6
|
+
private static readonly PROTECTED_COMMANDS;
|
|
7
|
+
/** Server/agent patterns — cannot match CLI processes, no self-kill risk. */
|
|
8
|
+
private static readonly SERVER_AGENT_PATTERNS;
|
|
6
9
|
/**
|
|
7
|
-
* Builds the list of
|
|
10
|
+
* Builds the list of CLI script patterns used to identify brv client processes.
|
|
8
11
|
*
|
|
9
12
|
* All patterns are absolute paths or specific filenames to avoid false-positive matches
|
|
10
13
|
* against other oclif CLIs (which also use bin/run.js and bin/dev.js conventions).
|
|
@@ -17,32 +20,14 @@ export default class Restart extends Command {
|
|
|
17
20
|
* nvm / system global: cmdline = node .../bin/brv ← caught by 'bin/brv' substring
|
|
18
21
|
* curl install (/.brv-cli/): join(brvBinDir, 'run') — entry point named 'run' without .js
|
|
19
22
|
*
|
|
20
|
-
* Relative patterns (./bin/run.js, ./bin/dev.js) are intentionally excluded: they would
|
|
21
|
-
* match any oclif CLI running in dev mode, not just brv.
|
|
22
|
-
*
|
|
23
23
|
* Set deduplicates when paths overlap (e.g. process.argv[1] is already run.js).
|
|
24
24
|
*/
|
|
25
|
-
static
|
|
26
|
-
/**
|
|
27
|
-
* Build a pid→cwd map from `lsof -d cwd -Fn` output.
|
|
28
|
-
*
|
|
29
|
-
* On macOS, `-p <pid>` is ignored and lsof returns ALL processes.
|
|
30
|
-
* Output format per process: `p<pid>\nfcwd\nn<cwd_path>`.
|
|
31
|
-
* Returns empty map if lsof is unavailable.
|
|
32
|
-
*/
|
|
33
|
-
private static buildCwdByPid;
|
|
25
|
+
static buildCliPatterns(): string[];
|
|
34
26
|
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* For processes started with a relative path (e.g. `./bin/dev.js`), the literal
|
|
38
|
-
* relative path is in the OS cmdline — absolute-path patterns won't match.
|
|
39
|
-
* Resolves relative .js paths using buildCwdByPid() to avoid false positives
|
|
40
|
-
* (e.g. `byterover-cli-clone/bin/dev.js` must not match `byterover-cli/bin/dev.js`).
|
|
41
|
-
*
|
|
42
|
-
* When cwd is resolved: check only absolute patterns (precise, no false positives).
|
|
43
|
-
* When cwd is unavailable: also check relative fallback patterns (./bin/dev.js).
|
|
27
|
+
* Returns true if the cmdline contains a protected command as an argument.
|
|
28
|
+
* Handles both /proc null-byte delimiters (Linux) and space delimiters (macOS ps).
|
|
44
29
|
*/
|
|
45
|
-
private static
|
|
30
|
+
private static isProtectedCommand;
|
|
46
31
|
/**
|
|
47
32
|
* Kill a process by PID.
|
|
48
33
|
* - Unix: SIGKILL via process.kill() — immediate, no graceful shutdown
|
|
@@ -51,46 +36,45 @@ export default class Restart extends Command {
|
|
|
51
36
|
private static killByPid;
|
|
52
37
|
/**
|
|
53
38
|
* Kill matching brv processes on Linux by scanning /proc/<pid>/cmdline.
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
* the path using /proc/<pid>/cwd so absolute-path patterns match correctly
|
|
57
|
-
* without false positives.
|
|
58
|
-
*
|
|
59
|
-
* When cwd is resolved: check only absolute patterns (precise, no false positives).
|
|
60
|
-
* When cwd is unavailable: also check relative fallback patterns (./bin/dev.js).
|
|
61
|
-
* Mirrors the macOS killByMacOsProcScan behavior.
|
|
62
|
-
*
|
|
63
|
-
* Works on all Linux distros including Alpine — /proc is a kernel feature,
|
|
64
|
-
* no userspace tools required.
|
|
39
|
+
* Simple substring match — no cwd resolution needed.
|
|
40
|
+
* Works on all Linux distros including Alpine — /proc is a kernel feature.
|
|
65
41
|
*/
|
|
66
42
|
private static killByProcScan;
|
|
67
43
|
/**
|
|
68
|
-
*
|
|
69
|
-
*
|
|
44
|
+
* Kill matching brv processes on macOS by scanning all processes via `ps`.
|
|
45
|
+
* Simple substring match — no cwd resolution needed because patterns
|
|
46
|
+
* are either unique filenames (brv-server.js) or absolute paths.
|
|
47
|
+
*/
|
|
48
|
+
private static killByPsScan;
|
|
49
|
+
/**
|
|
50
|
+
* Pattern-kill brv processes matching the given patterns.
|
|
51
|
+
*
|
|
52
|
+
* Self-exclusion: own PID and parent PID are always filtered out.
|
|
53
|
+
* The parent PID exclusion protects the oclif bin/brv bash wrapper
|
|
54
|
+
* on bundled installs (it does not use exec, so bash remains as parent).
|
|
70
55
|
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
56
|
+
* When skipProtected is true, processes running protected commands
|
|
57
|
+
* (e.g. `brv update`) are spared — prevents `brv restart` from killing
|
|
58
|
+
* the `brv update` process that invoked it.
|
|
73
59
|
*
|
|
74
60
|
* OS dispatch:
|
|
75
|
-
* Linux (incl. Alpine, WSL2): /proc scan
|
|
76
|
-
* macOS: ps -A scan
|
|
61
|
+
* Linux (incl. Alpine, WSL2): /proc scan
|
|
62
|
+
* macOS: ps -A scan
|
|
77
63
|
* Windows: PowerShell Get-CimInstance — available Windows 8+ / PS 3.0+
|
|
78
|
-
*
|
|
79
|
-
* Self-exclusion: own PID filtered on Unix; excluded explicitly in PowerShell query.
|
|
80
64
|
*/
|
|
81
65
|
private static patternKill;
|
|
82
66
|
private static sleep;
|
|
83
67
|
/**
|
|
84
|
-
* Polls until the process
|
|
68
|
+
* Polls until the process is dead, returning true if it exited within the timeout.
|
|
85
69
|
* Uses `process.kill(pid, 0)` — sends no signal, just checks existence.
|
|
86
|
-
* On ESRCH the PID is confirmed dead.
|
|
87
|
-
* outlives timeoutMs (e.g. zombie held by parent).
|
|
88
|
-
* Unix only — on Windows, taskkill /f is synchronous so no polling needed.
|
|
70
|
+
* On ESRCH the PID is confirmed dead.
|
|
89
71
|
*/
|
|
90
|
-
private static
|
|
72
|
+
private static waitForProcessExit;
|
|
91
73
|
protected cleanupAllDaemonFiles(dataDir: string): void;
|
|
92
74
|
protected exitProcess(code: number): void;
|
|
93
|
-
protected
|
|
75
|
+
protected loadDaemonInfo(dataDir: string): undefined | {
|
|
76
|
+
pid: number;
|
|
77
|
+
port: number;
|
|
78
|
+
};
|
|
94
79
|
run(): Promise<void>;
|
|
95
|
-
protected startDaemon(serverPath: string): Promise<EnsureDaemonResult>;
|
|
96
80
|
}
|
|
@@ -1,22 +1,25 @@
|
|
|
1
|
-
import { DAEMON_INSTANCE_FILE,
|
|
1
|
+
import { DAEMON_INSTANCE_FILE, getGlobalDataDir, GlobalInstanceManager, HEARTBEAT_FILE, SPAWN_LOCK_FILE, } from '@campfirein/brv-transport-client';
|
|
2
2
|
import { Command } from '@oclif/core';
|
|
3
3
|
import { spawnSync } from 'node:child_process';
|
|
4
|
-
import { readdirSync, readFileSync,
|
|
5
|
-
import { dirname, join } from 'node:path';
|
|
6
|
-
import { resolveLocalServerMainPath } from '../../server/utils/server-main-resolver.js';
|
|
7
|
-
const MAX_ATTEMPTS = 3;
|
|
4
|
+
import { readdirSync, readFileSync, unlinkSync } from 'node:fs';
|
|
5
|
+
import { dirname, join, resolve } from 'node:path';
|
|
8
6
|
const KILL_SETTLE_MS = 500;
|
|
9
|
-
const
|
|
10
|
-
const KILL_VERIFY_TIMEOUT_MS = 2000;
|
|
7
|
+
const KILL_VERIFY_TIMEOUT_MS = 5000;
|
|
11
8
|
const KILL_VERIFY_POLL_MS = 100;
|
|
9
|
+
const SIGTERM_BUDGET_MS = 8000;
|
|
12
10
|
export default class Restart extends Command {
|
|
13
11
|
static description = `Restart ByteRover — stop everything and start fresh.
|
|
14
12
|
|
|
15
13
|
Run this when ByteRover is unresponsive, stuck, or after installing an update.
|
|
16
|
-
All open sessions and background processes are stopped
|
|
14
|
+
All open sessions and background processes are stopped.
|
|
15
|
+
The daemon will restart automatically on the next brv command.`;
|
|
17
16
|
static examples = ['<%= config.bin %> <%= command.id %>'];
|
|
17
|
+
/** Commands whose processes must not be killed (e.g. `brv update` calls `brv restart`). */
|
|
18
|
+
static PROTECTED_COMMANDS = ['update'];
|
|
19
|
+
/** Server/agent patterns — cannot match CLI processes, no self-kill risk. */
|
|
20
|
+
static SERVER_AGENT_PATTERNS = ['brv-server.js', 'agent-process.js'];
|
|
18
21
|
/**
|
|
19
|
-
* Builds the list of
|
|
22
|
+
* Builds the list of CLI script patterns used to identify brv client processes.
|
|
20
23
|
*
|
|
21
24
|
* All patterns are absolute paths or specific filenames to avoid false-positive matches
|
|
22
25
|
* against other oclif CLIs (which also use bin/run.js and bin/dev.js conventions).
|
|
@@ -29,21 +32,12 @@ All open sessions and background processes are stopped before the fresh start.`;
|
|
|
29
32
|
* nvm / system global: cmdline = node .../bin/brv ← caught by 'bin/brv' substring
|
|
30
33
|
* curl install (/.brv-cli/): join(brvBinDir, 'run') — entry point named 'run' without .js
|
|
31
34
|
*
|
|
32
|
-
* Relative patterns (./bin/run.js, ./bin/dev.js) are intentionally excluded: they would
|
|
33
|
-
* match any oclif CLI running in dev mode, not just brv.
|
|
34
|
-
*
|
|
35
35
|
* Set deduplicates when paths overlap (e.g. process.argv[1] is already run.js).
|
|
36
36
|
*/
|
|
37
|
-
static
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
// in node_modules): /usr/local/.../byterover-cli/bin/run.js,
|
|
42
|
-
// .nvm/.../byterover-cli/bin/run.js. NOT used for dev.js because
|
|
43
|
-
// dev clones can have any directory name — covered by brvBinDir.
|
|
44
|
-
// exact sibling paths — current installation's run.js / dev.js / run (any dir name)
|
|
45
|
-
// process.argv[1] — current executable (bundled binary / dev entry)
|
|
46
|
-
const brvScripts = [
|
|
37
|
+
static buildCliPatterns() {
|
|
38
|
+
const argv1 = resolve(process.argv[1]);
|
|
39
|
+
const brvBinDir = dirname(argv1);
|
|
40
|
+
return [
|
|
47
41
|
...new Set([
|
|
48
42
|
argv1,
|
|
49
43
|
join('bin', 'brv'),
|
|
@@ -53,90 +47,19 @@ All open sessions and background processes are stopped before the fresh start.`;
|
|
|
53
47
|
join(brvBinDir, 'run.js'),
|
|
54
48
|
]),
|
|
55
49
|
];
|
|
56
|
-
return ['brv-server.js', 'agent-process.js', ...brvScripts];
|
|
57
50
|
}
|
|
58
51
|
/**
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* On macOS, `-p <pid>` is ignored and lsof returns ALL processes.
|
|
62
|
-
* Output format per process: `p<pid>\nfcwd\nn<cwd_path>`.
|
|
63
|
-
* Returns empty map if lsof is unavailable.
|
|
52
|
+
* Returns true if the cmdline contains a protected command as an argument.
|
|
53
|
+
* Handles both /proc null-byte delimiters (Linux) and space delimiters (macOS ps).
|
|
64
54
|
*/
|
|
65
|
-
static
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
for (let i = 0; i < lines.length; i++) {
|
|
74
|
-
if (lines[i].startsWith('p')) {
|
|
75
|
-
curPid = Number.parseInt(lines[i].slice(1), 10);
|
|
76
|
-
}
|
|
77
|
-
else if (lines[i] === 'fcwd' && curPid > 0 && lines[i + 1]?.startsWith('n')) {
|
|
78
|
-
cwdByPid.set(curPid, lines[i + 1].slice(1));
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
catch {
|
|
83
|
-
// lsof unavailable — caller falls back to relative-path patterns
|
|
84
|
-
}
|
|
85
|
-
return cwdByPid;
|
|
86
|
-
}
|
|
87
|
-
/**
|
|
88
|
-
* Kill matching brv processes on macOS by scanning all processes via `ps`.
|
|
89
|
-
*
|
|
90
|
-
* For processes started with a relative path (e.g. `./bin/dev.js`), the literal
|
|
91
|
-
* relative path is in the OS cmdline — absolute-path patterns won't match.
|
|
92
|
-
* Resolves relative .js paths using buildCwdByPid() to avoid false positives
|
|
93
|
-
* (e.g. `byterover-cli-clone/bin/dev.js` must not match `byterover-cli/bin/dev.js`).
|
|
94
|
-
*
|
|
95
|
-
* When cwd is resolved: check only absolute patterns (precise, no false positives).
|
|
96
|
-
* When cwd is unavailable: also check relative fallback patterns (./bin/dev.js).
|
|
97
|
-
*/
|
|
98
|
-
static killByMacOsProcScan(patterns, excludePids) {
|
|
99
|
-
const psResult = spawnSync('ps', ['-A', '-o', 'pid,args'], { encoding: 'utf8' });
|
|
100
|
-
if (!psResult.stdout)
|
|
101
|
-
return;
|
|
102
|
-
const cwdByPid = Restart.buildCwdByPid();
|
|
103
|
-
for (const line of psResult.stdout.split('\n').slice(1)) {
|
|
104
|
-
const match = /^\s*(\d+)\s+(.+)$/.exec(line);
|
|
105
|
-
if (!match)
|
|
106
|
-
continue;
|
|
107
|
-
const pid = Number.parseInt(match[1], 10);
|
|
108
|
-
const rawCmdline = match[2].trim();
|
|
109
|
-
if (Number.isNaN(pid) || excludePids.has(pid))
|
|
110
|
-
continue;
|
|
111
|
-
// Resolve relative .js path using cwd map to get an absolute path for matching.
|
|
112
|
-
let cmdline = rawCmdline;
|
|
113
|
-
let cwdResolved = false;
|
|
114
|
-
const relativeJs = rawCmdline.split(/\s+/).find((a) => a.endsWith('.js') && !a.startsWith('/'));
|
|
115
|
-
if (relativeJs) {
|
|
116
|
-
const cwd = cwdByPid.get(pid);
|
|
117
|
-
if (cwd) {
|
|
118
|
-
cmdline = rawCmdline.replace(relativeJs, join(cwd, relativeJs));
|
|
119
|
-
cwdResolved = true;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
for (const pattern of patterns) {
|
|
123
|
-
// When cwd resolved to absolute path, skip relative fallback patterns (those starting with
|
|
124
|
-
// './') — the resolved cmdline no longer contains relative paths, so these won't match.
|
|
125
|
-
// Prevents false positives against other projects (e.g. byterover-cli-clone) that also
|
|
126
|
-
// run ./bin/dev.js when lsof is unavailable and cwd cannot be resolved.
|
|
127
|
-
if (cwdResolved && pattern.startsWith('./'))
|
|
128
|
-
continue;
|
|
129
|
-
if (cmdline.includes(pattern)) {
|
|
130
|
-
try {
|
|
131
|
-
process.kill(pid, 'SIGKILL');
|
|
132
|
-
}
|
|
133
|
-
catch {
|
|
134
|
-
// Process already dead — ignore
|
|
135
|
-
}
|
|
136
|
-
break;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}
|
|
55
|
+
static isProtectedCommand(cmdline) {
|
|
56
|
+
return Restart.PROTECTED_COMMANDS.some((cmd) =>
|
|
57
|
+
// Linux /proc/cmdline: null-byte delimited
|
|
58
|
+
cmdline.includes(`\0${cmd}\0`) ||
|
|
59
|
+
cmdline.endsWith(`\0${cmd}`) ||
|
|
60
|
+
// macOS ps / Windows: space delimited
|
|
61
|
+
cmdline.endsWith(` ${cmd}`) ||
|
|
62
|
+
cmdline.includes(` ${cmd} `));
|
|
140
63
|
}
|
|
141
64
|
/**
|
|
142
65
|
* Kill a process by PID.
|
|
@@ -158,19 +81,10 @@ All open sessions and background processes are stopped before the fresh start.`;
|
|
|
158
81
|
}
|
|
159
82
|
/**
|
|
160
83
|
* Kill matching brv processes on Linux by scanning /proc/<pid>/cmdline.
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
* the path using /proc/<pid>/cwd so absolute-path patterns match correctly
|
|
164
|
-
* without false positives.
|
|
165
|
-
*
|
|
166
|
-
* When cwd is resolved: check only absolute patterns (precise, no false positives).
|
|
167
|
-
* When cwd is unavailable: also check relative fallback patterns (./bin/dev.js).
|
|
168
|
-
* Mirrors the macOS killByMacOsProcScan behavior.
|
|
169
|
-
*
|
|
170
|
-
* Works on all Linux distros including Alpine — /proc is a kernel feature,
|
|
171
|
-
* no userspace tools required.
|
|
84
|
+
* Simple substring match — no cwd resolution needed.
|
|
85
|
+
* Works on all Linux distros including Alpine — /proc is a kernel feature.
|
|
172
86
|
*/
|
|
173
|
-
static killByProcScan(patterns, excludePids) {
|
|
87
|
+
static killByProcScan(patterns, excludePids, skipProtected) {
|
|
174
88
|
let entries;
|
|
175
89
|
try {
|
|
176
90
|
entries = readdirSync('/proc');
|
|
@@ -183,34 +97,11 @@ All open sessions and background processes are stopped before the fresh start.`;
|
|
|
183
97
|
if (Number.isNaN(pid) || excludePids.has(pid))
|
|
184
98
|
continue;
|
|
185
99
|
try {
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
.
|
|
189
|
-
let cmdline = args.join(' ');
|
|
190
|
-
// Resolve relative .js path using /proc/<pid>/cwd to match against absolute-path patterns.
|
|
191
|
-
// Without this, `./bin/dev.js` would not match `byterover-cli/bin/dev.js`.
|
|
192
|
-
let cwdResolved = false;
|
|
193
|
-
const relativeJs = args.find((a) => a.endsWith('.js') && !a.startsWith('/'));
|
|
194
|
-
if (relativeJs) {
|
|
195
|
-
try {
|
|
196
|
-
const cwd = readlinkSync(join('/proc', entry, 'cwd'));
|
|
197
|
-
cmdline = cmdline.replace(relativeJs, join(cwd, relativeJs));
|
|
198
|
-
cwdResolved = true;
|
|
199
|
-
}
|
|
200
|
-
catch {
|
|
201
|
-
// cwd unreadable — use original cmdline
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
for (const pattern of patterns) {
|
|
205
|
-
// When cwd resolved to absolute path, skip relative fallback patterns (those starting with
|
|
206
|
-
// './') — the resolved cmdline no longer contains relative paths, so these won't match.
|
|
207
|
-
// Prevents false positives against other oclif CLIs that also run ./bin/dev.js.
|
|
208
|
-
if (cwdResolved && pattern.startsWith('./'))
|
|
100
|
+
const cmdline = readFileSync(join('/proc', entry, 'cmdline'), 'utf8');
|
|
101
|
+
if (patterns.some((p) => cmdline.includes(p))) {
|
|
102
|
+
if (skipProtected && Restart.isProtectedCommand(cmdline))
|
|
209
103
|
continue;
|
|
210
|
-
|
|
211
|
-
process.kill(pid, 'SIGKILL');
|
|
212
|
-
break; // Already killing this PID — no need to check remaining patterns
|
|
213
|
-
}
|
|
104
|
+
process.kill(pid, 'SIGKILL');
|
|
214
105
|
}
|
|
215
106
|
}
|
|
216
107
|
catch {
|
|
@@ -219,36 +110,66 @@ All open sessions and background processes are stopped before the fresh start.`;
|
|
|
219
110
|
}
|
|
220
111
|
}
|
|
221
112
|
/**
|
|
222
|
-
*
|
|
223
|
-
*
|
|
113
|
+
* Kill matching brv processes on macOS by scanning all processes via `ps`.
|
|
114
|
+
* Simple substring match — no cwd resolution needed because patterns
|
|
115
|
+
* are either unique filenames (brv-server.js) or absolute paths.
|
|
116
|
+
*/
|
|
117
|
+
static killByPsScan(patterns, excludePids, skipProtected) {
|
|
118
|
+
const psResult = spawnSync('ps', ['-A', '-o', 'pid,args'], { encoding: 'utf8' });
|
|
119
|
+
if (!psResult.stdout)
|
|
120
|
+
return;
|
|
121
|
+
for (const line of psResult.stdout.split('\n').slice(1)) {
|
|
122
|
+
const match = /^\s*(\d+)\s+(.+)$/.exec(line);
|
|
123
|
+
if (!match)
|
|
124
|
+
continue;
|
|
125
|
+
const pid = Number.parseInt(match[1], 10);
|
|
126
|
+
const cmdline = match[2];
|
|
127
|
+
if (Number.isNaN(pid) || excludePids.has(pid))
|
|
128
|
+
continue;
|
|
129
|
+
if (patterns.some((p) => cmdline.includes(p))) {
|
|
130
|
+
if (skipProtected && Restart.isProtectedCommand(cmdline))
|
|
131
|
+
continue;
|
|
132
|
+
try {
|
|
133
|
+
process.kill(pid, 'SIGKILL');
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
// Process already dead — ignore
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Pattern-kill brv processes matching the given patterns.
|
|
143
|
+
*
|
|
144
|
+
* Self-exclusion: own PID and parent PID are always filtered out.
|
|
145
|
+
* The parent PID exclusion protects the oclif bin/brv bash wrapper
|
|
146
|
+
* on bundled installs (it does not use exec, so bash remains as parent).
|
|
224
147
|
*
|
|
225
|
-
*
|
|
226
|
-
*
|
|
148
|
+
* When skipProtected is true, processes running protected commands
|
|
149
|
+
* (e.g. `brv update`) are spared — prevents `brv restart` from killing
|
|
150
|
+
* the `brv update` process that invoked it.
|
|
227
151
|
*
|
|
228
152
|
* OS dispatch:
|
|
229
|
-
* Linux (incl. Alpine, WSL2): /proc scan
|
|
230
|
-
* macOS: ps -A scan
|
|
153
|
+
* Linux (incl. Alpine, WSL2): /proc scan
|
|
154
|
+
* macOS: ps -A scan
|
|
231
155
|
* Windows: PowerShell Get-CimInstance — available Windows 8+ / PS 3.0+
|
|
232
|
-
*
|
|
233
|
-
* Self-exclusion: own PID filtered on Unix; excluded explicitly in PowerShell query.
|
|
234
156
|
*/
|
|
235
|
-
static patternKill() {
|
|
236
|
-
const brvBinDir = dirname(process.argv[1]);
|
|
237
|
-
const allPatterns = Restart.buildKillPatterns(brvBinDir, process.argv[1]);
|
|
238
|
-
// Exclude both the current node process and its parent shell wrapper (install.sh installs
|
|
239
|
-
// use a shell script that forks node — killing the wrapper garbles terminal output).
|
|
157
|
+
static patternKill(patterns, skipProtected = false) {
|
|
240
158
|
const excludePids = new Set([process.pid, process.ppid]);
|
|
241
159
|
if (process.platform === 'win32') {
|
|
242
|
-
const whereClause =
|
|
243
|
-
const
|
|
160
|
+
const whereClause = patterns.map((p) => `$_.CommandLine -like '*${p.replaceAll("'", "''")}*'`).join(' -or ');
|
|
161
|
+
const protectedClause = skipProtected
|
|
162
|
+
? ` -and ${Restart.PROTECTED_COMMANDS.map((cmd) => `$_.CommandLine -notlike '* ${cmd} *' -and $_.CommandLine -notlike '* ${cmd}'`).join(' -and ')}`
|
|
163
|
+
: '';
|
|
164
|
+
const script = `Get-CimInstance Win32_Process | Where-Object { (${whereClause}) -and $_.ProcessId -ne ${process.pid} -and $_.ProcessId -ne ${process.ppid}${protectedClause} } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }`;
|
|
244
165
|
spawnSync('powershell', ['-Command', script], { stdio: 'ignore' });
|
|
245
166
|
}
|
|
246
167
|
else if (process.platform === 'linux') {
|
|
247
|
-
Restart.killByProcScan(
|
|
168
|
+
Restart.killByProcScan(patterns, excludePids, skipProtected);
|
|
248
169
|
}
|
|
249
170
|
else {
|
|
250
|
-
// macOS (and other Unix): ps -A scan
|
|
251
|
-
Restart.
|
|
171
|
+
// macOS (and other Unix): ps -A scan
|
|
172
|
+
Restart.killByPsScan(patterns, excludePids, skipProtected);
|
|
252
173
|
}
|
|
253
174
|
}
|
|
254
175
|
static sleep(ms) {
|
|
@@ -257,25 +178,23 @@ All open sessions and background processes are stopped before the fresh start.`;
|
|
|
257
178
|
});
|
|
258
179
|
}
|
|
259
180
|
/**
|
|
260
|
-
* Polls until the process
|
|
181
|
+
* Polls until the process is dead, returning true if it exited within the timeout.
|
|
261
182
|
* Uses `process.kill(pid, 0)` — sends no signal, just checks existence.
|
|
262
|
-
* On ESRCH the PID is confirmed dead.
|
|
263
|
-
* outlives timeoutMs (e.g. zombie held by parent).
|
|
264
|
-
* Unix only — on Windows, taskkill /f is synchronous so no polling needed.
|
|
183
|
+
* On ESRCH the PID is confirmed dead.
|
|
265
184
|
*/
|
|
266
|
-
static async
|
|
185
|
+
static async waitForProcessExit(pid, timeoutMs) {
|
|
267
186
|
const deadline = Date.now() + timeoutMs;
|
|
268
187
|
while (Date.now() < deadline) {
|
|
269
188
|
try {
|
|
270
|
-
process.kill(pid, 0);
|
|
189
|
+
process.kill(pid, 0);
|
|
271
190
|
}
|
|
272
191
|
catch {
|
|
273
|
-
return; //
|
|
192
|
+
return true; // ESRCH = dead
|
|
274
193
|
}
|
|
275
194
|
// eslint-disable-next-line no-await-in-loop -- intentional poll loop
|
|
276
195
|
await Restart.sleep(KILL_VERIFY_POLL_MS);
|
|
277
196
|
}
|
|
278
|
-
|
|
197
|
+
return false;
|
|
279
198
|
}
|
|
280
199
|
cleanupAllDaemonFiles(dataDir) {
|
|
281
200
|
for (const file of [DAEMON_INSTANCE_FILE, HEARTBEAT_FILE, SPAWN_LOCK_FILE]) {
|
|
@@ -291,55 +210,49 @@ All open sessions and background processes are stopped before the fresh start.`;
|
|
|
291
210
|
// eslint-disable-next-line n/no-process-exit, unicorn/no-process-exit
|
|
292
211
|
process.exit(code);
|
|
293
212
|
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
// Read PID directly from daemon.json — no health-check filtering.
|
|
297
|
-
const info = new GlobalInstanceManager({ dataDir }).load();
|
|
298
|
-
if (info !== undefined) {
|
|
299
|
-
Restart.killByPid(info.pid);
|
|
300
|
-
// Verify the daemon PID is dead before pattern-killing the rest.
|
|
301
|
-
// taskkill /f on Windows is synchronous so polling is only needed on Unix.
|
|
302
|
-
if (process.platform !== 'win32') {
|
|
303
|
-
await Restart.waitForPidToDie(info.pid, KILL_VERIFY_TIMEOUT_MS);
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
// Always run pattern kill — catches processes not in daemon.json
|
|
307
|
-
// (agents, TUI sessions, MCP servers, headless commands).
|
|
308
|
-
Restart.patternKill();
|
|
309
|
-
await Restart.sleep(KILL_SETTLE_MS);
|
|
213
|
+
loadDaemonInfo(dataDir) {
|
|
214
|
+
return new GlobalInstanceManager({ dataDir }).load();
|
|
310
215
|
}
|
|
311
216
|
async run() {
|
|
312
|
-
const serverPath = resolveLocalServerMainPath();
|
|
313
217
|
const dataDir = getGlobalDataDir();
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
218
|
+
// Phase 1: Kill all client processes first (TUI, MCP, headless commands).
|
|
219
|
+
// Must happen BEFORE daemon kill — clients have reconnectors that will
|
|
220
|
+
// respawn the daemon via ensureDaemonRunning() if they detect disconnection.
|
|
221
|
+
// Self excluded by process.pid / process.ppid.
|
|
222
|
+
// Protected commands (e.g. `brv update`) are spared.
|
|
223
|
+
this.log('Stopping clients...');
|
|
224
|
+
Restart.patternKill(Restart.buildCliPatterns(), true);
|
|
225
|
+
await Restart.sleep(KILL_SETTLE_MS);
|
|
226
|
+
// Phase 2: Graceful daemon kill via daemon.json PID.
|
|
227
|
+
// SIGTERM triggers ShutdownHandler → stops agents, transport, releases daemon.json.
|
|
228
|
+
// Safe now because all clients are dead — no one can respawn daemon.
|
|
229
|
+
const info = this.loadDaemonInfo(dataDir);
|
|
230
|
+
if (info !== undefined) {
|
|
231
|
+
this.log(`Stopping daemon (PID ${info.pid})...`);
|
|
232
|
+
let stopped = false;
|
|
233
|
+
try {
|
|
234
|
+
process.kill(info.pid, 'SIGTERM');
|
|
235
|
+
stopped = await Restart.waitForProcessExit(info.pid, SIGTERM_BUDGET_MS);
|
|
326
236
|
}
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
this.log(`Daemon did not start (${result.reason}${detail}). Retrying...`);
|
|
237
|
+
catch {
|
|
238
|
+
stopped = true; // ESRCH = already dead
|
|
330
239
|
}
|
|
331
|
-
|
|
332
|
-
|
|
240
|
+
if (!stopped) {
|
|
241
|
+
Restart.killByPid(info.pid);
|
|
242
|
+
if (process.platform !== 'win32') {
|
|
243
|
+
await Restart.waitForProcessExit(info.pid, KILL_VERIFY_TIMEOUT_MS);
|
|
244
|
+
}
|
|
333
245
|
}
|
|
334
246
|
}
|
|
335
|
-
|
|
247
|
+
// Phase 3: Kill orphaned server/agent processes not tracked in daemon.json.
|
|
248
|
+
Restart.patternKill(Restart.SERVER_AGENT_PATTERNS);
|
|
249
|
+
await Restart.sleep(KILL_SETTLE_MS);
|
|
250
|
+
// Phase 4: Clean state files.
|
|
251
|
+
this.cleanupAllDaemonFiles(dataDir);
|
|
252
|
+
this.log('All ByteRover processes stopped.');
|
|
336
253
|
// Force exit — oclif does not call process.exit() after run() returns,
|
|
337
|
-
// relying on the event loop to drain.
|
|
338
|
-
//
|
|
339
|
-
// the process from exiting naturally. mcp.ts uses the same pattern.
|
|
254
|
+
// relying on the event loop to drain. Third-party plugin hooks (e.g.
|
|
255
|
+
// @oclif/plugin-update) can leave open handles that prevent exit.
|
|
340
256
|
this.exitProcess(0);
|
|
341
257
|
}
|
|
342
|
-
async startDaemon(serverPath) {
|
|
343
|
-
return ensureDaemonRunning({ serverPath, timeoutMs: DAEMON_START_TIMEOUT_MS });
|
|
344
|
-
}
|
|
345
258
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Hook } from '@oclif/core';
|
|
2
|
+
export type BlockCommandUpdateNpmDeps = {
|
|
3
|
+
commandId: string | undefined;
|
|
4
|
+
errorFn: (message: string, options: {
|
|
5
|
+
exit: number;
|
|
6
|
+
}) => void;
|
|
7
|
+
isNpmGlobalInstalled: boolean;
|
|
8
|
+
};
|
|
9
|
+
export declare function handleBlockCommandUpdateNpm(deps: BlockCommandUpdateNpmDeps): void;
|
|
10
|
+
declare const hook: Hook<'init'>;
|
|
11
|
+
export default hook;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
import { isNpmGlobalInstall } from './update-notifier.js';
|
|
3
|
+
export function handleBlockCommandUpdateNpm(deps) {
|
|
4
|
+
if (deps.commandId === 'update' && deps.isNpmGlobalInstalled) {
|
|
5
|
+
deps.errorFn('brv was installed via npm. Use `npm update -g byterover-cli` to update.', { exit: 1 });
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
const hook = async function (opts) {
|
|
9
|
+
handleBlockCommandUpdateNpm({
|
|
10
|
+
commandId: opts.id,
|
|
11
|
+
errorFn: this.error.bind(this),
|
|
12
|
+
isNpmGlobalInstalled: isNpmGlobalInstall(execSync),
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
export default hook;
|
|
@@ -33,6 +33,9 @@ export type UpdateNotifierDeps = {
|
|
|
33
33
|
isTTY: boolean;
|
|
34
34
|
log: (message: string) => void;
|
|
35
35
|
notifier: NarrowedUpdateNotifier;
|
|
36
|
+
spawnRestartFn: () => {
|
|
37
|
+
unref(): void;
|
|
38
|
+
};
|
|
36
39
|
};
|
|
37
40
|
/**
|
|
38
41
|
* Check whether byterover-cli is installed as a npm global package.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { confirm } from '@inquirer/prompts';
|
|
2
|
-
import { execSync } from 'node:child_process';
|
|
2
|
+
import { execSync, spawn } from 'node:child_process';
|
|
3
3
|
import updateNotifier from 'update-notifier';
|
|
4
4
|
/**
|
|
5
5
|
* Check interval for update notifications (1 hour)
|
|
@@ -34,16 +34,23 @@ export async function handleUpdateNotification(deps) {
|
|
|
34
34
|
}
|
|
35
35
|
const shouldUpdate = await confirmPrompt({
|
|
36
36
|
default: true,
|
|
37
|
-
message: `Update available: ${current} → ${latest}.
|
|
37
|
+
message: `Update available: ${current} → ${latest}. Update now? (active sessions will be restarted)`,
|
|
38
38
|
});
|
|
39
39
|
if (shouldUpdate) {
|
|
40
40
|
log('Updating byterover-cli...');
|
|
41
41
|
try {
|
|
42
42
|
execSyncFn('npm update -g byterover-cli', { stdio: 'inherit' });
|
|
43
43
|
log('');
|
|
44
|
-
log(`✓
|
|
44
|
+
log(`✓ Updated to ${latest}.`);
|
|
45
45
|
log('');
|
|
46
|
-
|
|
46
|
+
try {
|
|
47
|
+
const child = deps.spawnRestartFn();
|
|
48
|
+
child.unref();
|
|
49
|
+
log('Restarting ByteRover in the background. Please wait a few seconds before running brv again.');
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
log('Failed to restart ByteRover. Please restart it manually by running `brv restart`.');
|
|
53
|
+
}
|
|
47
54
|
exitFn(0);
|
|
48
55
|
}
|
|
49
56
|
catch {
|
|
@@ -63,6 +70,12 @@ const hook = async function () {
|
|
|
63
70
|
isTTY: process.stdout.isTTY ?? false,
|
|
64
71
|
log: this.log.bind(this),
|
|
65
72
|
notifier,
|
|
73
|
+
spawnRestartFn: () => spawn('brv restart', {
|
|
74
|
+
detached: true,
|
|
75
|
+
shell: true,
|
|
76
|
+
stdio: 'ignore',
|
|
77
|
+
windowsHide: true,
|
|
78
|
+
}),
|
|
66
79
|
});
|
|
67
80
|
};
|
|
68
81
|
export default hook;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Hook } from '@oclif/core';
|
|
2
|
+
export type RestartAfterUpdateDeps = {
|
|
3
|
+
argv: string[];
|
|
4
|
+
commandId: string | undefined;
|
|
5
|
+
log: (msg: string) => void;
|
|
6
|
+
spawnRestartFn: () => {
|
|
7
|
+
unref(): void;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Restart daemon/agent processes after a manual `brv update`.
|
|
12
|
+
*
|
|
13
|
+
* Fires after every command via the oclif postrun hook; early-returns for
|
|
14
|
+
* anything other than `brv update`.
|
|
15
|
+
*
|
|
16
|
+
* Skips background auto-updates: @oclif/plugin-update passes `--autoupdate`
|
|
17
|
+
* when spawning `brv update` in the background. Manual `brv update` runs
|
|
18
|
+
* in the user's shell without that flag.
|
|
19
|
+
*/
|
|
20
|
+
export declare function handleRestartAfterUpdate(deps: RestartAfterUpdateDeps): Promise<void>;
|
|
21
|
+
declare const hook: Hook<'postrun'>;
|
|
22
|
+
export default hook;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
/**
|
|
3
|
+
* Restart daemon/agent processes after a manual `brv update`.
|
|
4
|
+
*
|
|
5
|
+
* Fires after every command via the oclif postrun hook; early-returns for
|
|
6
|
+
* anything other than `brv update`.
|
|
7
|
+
*
|
|
8
|
+
* Skips background auto-updates: @oclif/plugin-update passes `--autoupdate`
|
|
9
|
+
* when spawning `brv update` in the background. Manual `brv update` runs
|
|
10
|
+
* in the user's shell without that flag.
|
|
11
|
+
*/
|
|
12
|
+
export async function handleRestartAfterUpdate(deps) {
|
|
13
|
+
if (deps.commandId !== 'update')
|
|
14
|
+
return;
|
|
15
|
+
if (deps.argv.includes('--autoupdate'))
|
|
16
|
+
return;
|
|
17
|
+
try {
|
|
18
|
+
const child = deps.spawnRestartFn();
|
|
19
|
+
child.unref();
|
|
20
|
+
deps.log('Restarting ByteRover in the background. Please wait a few seconds before running brv again.');
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
deps.log('Failed to restart ByteRover. Please restart it manually by running `brv restart`.');
|
|
24
|
+
// best-effort — update already succeeded
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const hook = async function (opts) {
|
|
28
|
+
await handleRestartAfterUpdate({
|
|
29
|
+
argv: opts.argv,
|
|
30
|
+
commandId: opts.Command.id,
|
|
31
|
+
log: this.log.bind(this),
|
|
32
|
+
spawnRestartFn: () => spawn('brv restart', {
|
|
33
|
+
detached: true,
|
|
34
|
+
shell: true,
|
|
35
|
+
stdio: 'ignore',
|
|
36
|
+
windowsHide: true,
|
|
37
|
+
}),
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
export default hook;
|
package/oclif.manifest.json
CHANGED
|
@@ -403,7 +403,7 @@
|
|
|
403
403
|
"restart": {
|
|
404
404
|
"aliases": [],
|
|
405
405
|
"args": {},
|
|
406
|
-
"description": "Restart ByteRover — stop everything and start fresh.\n\nRun this when ByteRover is unresponsive, stuck, or after installing an update.\nAll open sessions and background processes are stopped
|
|
406
|
+
"description": "Restart ByteRover — stop everything and start fresh.\n\nRun this when ByteRover is unresponsive, stuck, or after installing an update.\nAll open sessions and background processes are stopped.\nThe daemon will restart automatically on the next brv command.",
|
|
407
407
|
"examples": [
|
|
408
408
|
"<%= config.bin %> <%= command.id %>"
|
|
409
409
|
],
|
|
@@ -416,6 +416,13 @@
|
|
|
416
416
|
"pluginType": "core",
|
|
417
417
|
"strict": true,
|
|
418
418
|
"enableJsonFlag": false,
|
|
419
|
+
"PROTECTED_COMMANDS": [
|
|
420
|
+
"update"
|
|
421
|
+
],
|
|
422
|
+
"SERVER_AGENT_PATTERNS": [
|
|
423
|
+
"brv-server.js",
|
|
424
|
+
"agent-process.js"
|
|
425
|
+
],
|
|
419
426
|
"isESM": true,
|
|
420
427
|
"relativePath": [
|
|
421
428
|
"dist",
|
|
@@ -1076,6 +1083,104 @@
|
|
|
1076
1083
|
"switch.js"
|
|
1077
1084
|
]
|
|
1078
1085
|
},
|
|
1086
|
+
"space:list": {
|
|
1087
|
+
"aliases": [],
|
|
1088
|
+
"args": {},
|
|
1089
|
+
"description": "List all teams and spaces",
|
|
1090
|
+
"examples": [
|
|
1091
|
+
"<%= config.bin %> space list",
|
|
1092
|
+
"<%= config.bin %> space list --format json"
|
|
1093
|
+
],
|
|
1094
|
+
"flags": {
|
|
1095
|
+
"format": {
|
|
1096
|
+
"char": "f",
|
|
1097
|
+
"description": "Output format",
|
|
1098
|
+
"name": "format",
|
|
1099
|
+
"default": "text",
|
|
1100
|
+
"hasDynamicHelp": false,
|
|
1101
|
+
"multiple": false,
|
|
1102
|
+
"options": [
|
|
1103
|
+
"text",
|
|
1104
|
+
"json"
|
|
1105
|
+
],
|
|
1106
|
+
"type": "option"
|
|
1107
|
+
}
|
|
1108
|
+
},
|
|
1109
|
+
"hasDynamicHelp": false,
|
|
1110
|
+
"hiddenAliases": [],
|
|
1111
|
+
"id": "space:list",
|
|
1112
|
+
"pluginAlias": "byterover-cli",
|
|
1113
|
+
"pluginName": "byterover-cli",
|
|
1114
|
+
"pluginType": "core",
|
|
1115
|
+
"strict": true,
|
|
1116
|
+
"enableJsonFlag": false,
|
|
1117
|
+
"isESM": true,
|
|
1118
|
+
"relativePath": [
|
|
1119
|
+
"dist",
|
|
1120
|
+
"oclif",
|
|
1121
|
+
"commands",
|
|
1122
|
+
"space",
|
|
1123
|
+
"list.js"
|
|
1124
|
+
]
|
|
1125
|
+
},
|
|
1126
|
+
"space:switch": {
|
|
1127
|
+
"aliases": [],
|
|
1128
|
+
"args": {},
|
|
1129
|
+
"description": "Switch to a different space",
|
|
1130
|
+
"examples": [
|
|
1131
|
+
"<%= config.bin %> space switch --team acme --name my-space",
|
|
1132
|
+
"<%= config.bin %> space switch --team acme --name my-space --format json"
|
|
1133
|
+
],
|
|
1134
|
+
"flags": {
|
|
1135
|
+
"format": {
|
|
1136
|
+
"char": "f",
|
|
1137
|
+
"description": "Output format",
|
|
1138
|
+
"name": "format",
|
|
1139
|
+
"default": "text",
|
|
1140
|
+
"hasDynamicHelp": false,
|
|
1141
|
+
"multiple": false,
|
|
1142
|
+
"options": [
|
|
1143
|
+
"text",
|
|
1144
|
+
"json"
|
|
1145
|
+
],
|
|
1146
|
+
"type": "option"
|
|
1147
|
+
},
|
|
1148
|
+
"name": {
|
|
1149
|
+
"char": "n",
|
|
1150
|
+
"description": "Name of the space to switch to",
|
|
1151
|
+
"name": "name",
|
|
1152
|
+
"required": true,
|
|
1153
|
+
"hasDynamicHelp": false,
|
|
1154
|
+
"multiple": false,
|
|
1155
|
+
"type": "option"
|
|
1156
|
+
},
|
|
1157
|
+
"team": {
|
|
1158
|
+
"char": "t",
|
|
1159
|
+
"description": "Team name",
|
|
1160
|
+
"name": "team",
|
|
1161
|
+
"required": true,
|
|
1162
|
+
"hasDynamicHelp": false,
|
|
1163
|
+
"multiple": false,
|
|
1164
|
+
"type": "option"
|
|
1165
|
+
}
|
|
1166
|
+
},
|
|
1167
|
+
"hasDynamicHelp": false,
|
|
1168
|
+
"hiddenAliases": [],
|
|
1169
|
+
"id": "space:switch",
|
|
1170
|
+
"pluginAlias": "byterover-cli",
|
|
1171
|
+
"pluginName": "byterover-cli",
|
|
1172
|
+
"pluginType": "core",
|
|
1173
|
+
"strict": true,
|
|
1174
|
+
"enableJsonFlag": false,
|
|
1175
|
+
"isESM": true,
|
|
1176
|
+
"relativePath": [
|
|
1177
|
+
"dist",
|
|
1178
|
+
"oclif",
|
|
1179
|
+
"commands",
|
|
1180
|
+
"space",
|
|
1181
|
+
"switch.js"
|
|
1182
|
+
]
|
|
1183
|
+
},
|
|
1079
1184
|
"providers:connect": {
|
|
1080
1185
|
"aliases": [],
|
|
1081
1186
|
"args": {
|
|
@@ -1332,104 +1437,6 @@
|
|
|
1332
1437
|
"switch.js"
|
|
1333
1438
|
]
|
|
1334
1439
|
},
|
|
1335
|
-
"space:list": {
|
|
1336
|
-
"aliases": [],
|
|
1337
|
-
"args": {},
|
|
1338
|
-
"description": "List all teams and spaces",
|
|
1339
|
-
"examples": [
|
|
1340
|
-
"<%= config.bin %> space list",
|
|
1341
|
-
"<%= config.bin %> space list --format json"
|
|
1342
|
-
],
|
|
1343
|
-
"flags": {
|
|
1344
|
-
"format": {
|
|
1345
|
-
"char": "f",
|
|
1346
|
-
"description": "Output format",
|
|
1347
|
-
"name": "format",
|
|
1348
|
-
"default": "text",
|
|
1349
|
-
"hasDynamicHelp": false,
|
|
1350
|
-
"multiple": false,
|
|
1351
|
-
"options": [
|
|
1352
|
-
"text",
|
|
1353
|
-
"json"
|
|
1354
|
-
],
|
|
1355
|
-
"type": "option"
|
|
1356
|
-
}
|
|
1357
|
-
},
|
|
1358
|
-
"hasDynamicHelp": false,
|
|
1359
|
-
"hiddenAliases": [],
|
|
1360
|
-
"id": "space:list",
|
|
1361
|
-
"pluginAlias": "byterover-cli",
|
|
1362
|
-
"pluginName": "byterover-cli",
|
|
1363
|
-
"pluginType": "core",
|
|
1364
|
-
"strict": true,
|
|
1365
|
-
"enableJsonFlag": false,
|
|
1366
|
-
"isESM": true,
|
|
1367
|
-
"relativePath": [
|
|
1368
|
-
"dist",
|
|
1369
|
-
"oclif",
|
|
1370
|
-
"commands",
|
|
1371
|
-
"space",
|
|
1372
|
-
"list.js"
|
|
1373
|
-
]
|
|
1374
|
-
},
|
|
1375
|
-
"space:switch": {
|
|
1376
|
-
"aliases": [],
|
|
1377
|
-
"args": {},
|
|
1378
|
-
"description": "Switch to a different space",
|
|
1379
|
-
"examples": [
|
|
1380
|
-
"<%= config.bin %> space switch --team acme --name my-space",
|
|
1381
|
-
"<%= config.bin %> space switch --team acme --name my-space --format json"
|
|
1382
|
-
],
|
|
1383
|
-
"flags": {
|
|
1384
|
-
"format": {
|
|
1385
|
-
"char": "f",
|
|
1386
|
-
"description": "Output format",
|
|
1387
|
-
"name": "format",
|
|
1388
|
-
"default": "text",
|
|
1389
|
-
"hasDynamicHelp": false,
|
|
1390
|
-
"multiple": false,
|
|
1391
|
-
"options": [
|
|
1392
|
-
"text",
|
|
1393
|
-
"json"
|
|
1394
|
-
],
|
|
1395
|
-
"type": "option"
|
|
1396
|
-
},
|
|
1397
|
-
"name": {
|
|
1398
|
-
"char": "n",
|
|
1399
|
-
"description": "Name of the space to switch to",
|
|
1400
|
-
"name": "name",
|
|
1401
|
-
"required": true,
|
|
1402
|
-
"hasDynamicHelp": false,
|
|
1403
|
-
"multiple": false,
|
|
1404
|
-
"type": "option"
|
|
1405
|
-
},
|
|
1406
|
-
"team": {
|
|
1407
|
-
"char": "t",
|
|
1408
|
-
"description": "Team name",
|
|
1409
|
-
"name": "team",
|
|
1410
|
-
"required": true,
|
|
1411
|
-
"hasDynamicHelp": false,
|
|
1412
|
-
"multiple": false,
|
|
1413
|
-
"type": "option"
|
|
1414
|
-
}
|
|
1415
|
-
},
|
|
1416
|
-
"hasDynamicHelp": false,
|
|
1417
|
-
"hiddenAliases": [],
|
|
1418
|
-
"id": "space:switch",
|
|
1419
|
-
"pluginAlias": "byterover-cli",
|
|
1420
|
-
"pluginName": "byterover-cli",
|
|
1421
|
-
"pluginType": "core",
|
|
1422
|
-
"strict": true,
|
|
1423
|
-
"enableJsonFlag": false,
|
|
1424
|
-
"isESM": true,
|
|
1425
|
-
"relativePath": [
|
|
1426
|
-
"dist",
|
|
1427
|
-
"oclif",
|
|
1428
|
-
"commands",
|
|
1429
|
-
"space",
|
|
1430
|
-
"switch.js"
|
|
1431
|
-
]
|
|
1432
|
-
},
|
|
1433
1440
|
"hub:registry:add": {
|
|
1434
1441
|
"aliases": [],
|
|
1435
1442
|
"args": {
|
|
@@ -1629,5 +1636,5 @@
|
|
|
1629
1636
|
]
|
|
1630
1637
|
}
|
|
1631
1638
|
},
|
|
1632
|
-
"version": "2.3.
|
|
1639
|
+
"version": "2.3.1"
|
|
1633
1640
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "byterover-cli",
|
|
3
3
|
"description": "ByteRover's CLI",
|
|
4
|
-
"version": "2.3.
|
|
4
|
+
"version": "2.3.1",
|
|
5
5
|
"author": "ByteRover",
|
|
6
6
|
"bin": {
|
|
7
7
|
"brv": "./bin/run.js"
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"@ai-sdk/vercel": "^1.0.33",
|
|
23
23
|
"@ai-sdk/xai": "^2.0.57",
|
|
24
24
|
"@anthropic-ai/sdk": "^0.70.1",
|
|
25
|
-
"@campfirein/brv-transport-client": "github:campfirein/brv-transport-client#release/0.8.
|
|
25
|
+
"@campfirein/brv-transport-client": "github:campfirein/brv-transport-client#release/0.8.4",
|
|
26
26
|
"@google/genai": "^1.29.0",
|
|
27
27
|
"@inkjs/ui": "^2.0.0",
|
|
28
28
|
"@inquirer/prompts": "^7.9.0",
|
|
@@ -59,8 +59,8 @@
|
|
|
59
59
|
"react": "^19.2.1",
|
|
60
60
|
"react-router-dom": "^7.13.0",
|
|
61
61
|
"remark-parse": "^11.0.0",
|
|
62
|
-
"socket.io": "^4.8.
|
|
63
|
-
"socket.io-client": "^4.8.
|
|
62
|
+
"socket.io": "^4.8.3",
|
|
63
|
+
"socket.io-client": "^4.8.3",
|
|
64
64
|
"stopword": "^3.1.5",
|
|
65
65
|
"unified": "^11.0.5",
|
|
66
66
|
"unpdf": "^1.4.0",
|
|
@@ -125,12 +125,14 @@
|
|
|
125
125
|
"commands": "./dist/oclif/commands",
|
|
126
126
|
"hooks": {
|
|
127
127
|
"init": [
|
|
128
|
+
"./dist/oclif/hooks/init/block-command-update-npm",
|
|
128
129
|
"./dist/oclif/hooks/init/welcome",
|
|
129
130
|
"./dist/oclif/hooks/init/update-notifier"
|
|
130
131
|
],
|
|
131
132
|
"command_not_found": "./dist/oclif/hooks/command_not_found/handle-invalid-commands",
|
|
132
133
|
"error": "./dist/oclif/hooks/error/clean-errors",
|
|
133
|
-
"prerun": "./dist/oclif/hooks/prerun/validate-brv-config-version"
|
|
134
|
+
"prerun": "./dist/oclif/hooks/prerun/validate-brv-config-version",
|
|
135
|
+
"postrun": "./dist/oclif/hooks/postrun/restart-after-update"
|
|
134
136
|
},
|
|
135
137
|
"plugins": [
|
|
136
138
|
"@oclif/plugin-help",
|