@polderlabs/bizar 4.5.1 → 4.7.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/bizar-dash/dist/assets/__vite-browser-external-BIHI7g3E.js +0 -1
- package/bizar-dash/dist/assets/main-DAlLdW8I.css +1 -0
- package/bizar-dash/dist/assets/main-DGGq-iZI.js +361 -0
- package/bizar-dash/dist/assets/main-DGGq-iZI.js.map +1 -0
- package/bizar-dash/dist/assets/{mobile-O6ANdD4W.js → mobile-CWqPoGaT.js} +18 -19
- package/bizar-dash/dist/assets/mobile-CWqPoGaT.js.map +1 -0
- package/bizar-dash/dist/assets/{mobile-DQLFCjwJ.js → mobile-i4Uv9eW8.js} +1 -2
- package/bizar-dash/dist/assets/{mobile-DQLFCjwJ.js.map → mobile-i4Uv9eW8.js.map} +1 -1
- package/bizar-dash/dist/index.html +3 -3
- package/bizar-dash/dist/mobile.html +2 -2
- package/bizar-dash/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/bizar-dash/src/server/headroom.mjs +37 -35
- package/bizar-dash/src/server/logger.mjs +71 -0
- package/bizar-dash/src/server/memory-lightrag.mjs +84 -37
- package/bizar-dash/src/server/metrics.mjs +193 -0
- package/bizar-dash/src/server/mods-loader.mjs +13 -5
- package/bizar-dash/src/server/providers-store.mjs +73 -1
- package/bizar-dash/src/server/routes/chat.mjs +41 -0
- package/bizar-dash/src/server/routes/lightrag.mjs +7 -2
- package/bizar-dash/src/server/routes/memory.mjs +13 -4
- package/bizar-dash/src/server/routes/misc.mjs +2 -1
- package/bizar-dash/src/server/routes/overview.mjs +2 -1
- package/bizar-dash/src/server/schedules-runner.mjs +4 -4
- package/bizar-dash/src/server/server.mjs +101 -37
- package/bizar-dash/src/server/watcher.mjs +2 -2
- package/bizar-dash/src/web/App.tsx +40 -6
- package/bizar-dash/src/web/components/SearchModal.tsx +3 -2
- package/bizar-dash/src/web/components/Toast.tsx +1 -1
- package/bizar-dash/src/web/components/Topbar.tsx +1 -1
- package/bizar-dash/src/web/components/VirtualList.tsx +53 -0
- package/bizar-dash/src/web/components/chat/ChatThread.tsx +17 -11
- package/bizar-dash/src/web/hooks/useI18n.ts +13 -0
- package/bizar-dash/src/web/lib/api.ts +12 -11
- package/bizar-dash/src/web/lib/i18n.ts +25 -0
- package/bizar-dash/src/web/locales/en.json +52 -0
- package/bizar-dash/src/web/main.tsx +5 -0
- package/bizar-dash/src/web/styles/chat.css +2 -0
- package/bizar-dash/src/web/styles/main.css +52 -23
- package/bizar-dash/src/web/styles/tasks.css +2 -0
- package/bizar-dash/src/web/views/Activity.tsx +24 -17
- package/bizar-dash/src/web/views/History.tsx +90 -75
- package/bizar-dash/src/web/views/Memory.tsx +4 -3
- package/bizar-dash/src/web/views/MiniMaxUsage.tsx +3 -2
- package/bizar-dash/src/web/views/Overview.tsx +15 -10
- package/bizar-dash/src/web/views/Settings.tsx +9 -2
- package/bizar-dash/src/web/views/Skills.tsx +3 -2
- package/bizar-dash/src/web/views/Tasks.tsx +4 -3
- package/bizar-dash/src/web/views/memory/MemoryOverview.tsx +8 -6
- package/bizar-dash/tests/cli-bugfixes.test.mjs +151 -0
- package/bizar-dash/tests/cli-refactor.test.mjs +184 -0
- package/bizar-dash/tests/components/Button.test.tsx +41 -0
- package/bizar-dash/tests/components/Card.test.tsx +42 -0
- package/bizar-dash/tests/components/Modal.test.tsx +104 -0
- package/bizar-dash/tests/components/Spinner.test.tsx +32 -0
- package/bizar-dash/tests/components/StatusBadge.test.tsx +35 -0
- package/bizar-dash/tests/components/Toast.test.tsx +108 -0
- package/bizar-dash/tests/frontend-bugfixes.test.mjs +151 -0
- package/bizar-dash/tests/hooks/useModal.test.tsx +84 -0
- package/bizar-dash/tests/hooks/useToast.test.tsx +50 -0
- package/bizar-dash/tests/lib/i18n.test.ts +46 -0
- package/bizar-dash/tests/lib/utils.test.ts +194 -0
- package/bizar-dash/tests/logger.test.mjs +207 -0
- package/bizar-dash/tests/metrics.test.mjs +183 -0
- package/bizar-dash/tests/server-bugfixes.test.mjs +318 -0
- package/bizar-dash/tests/setup.ts +7 -0
- package/bizar-dash/vitest.config.ts +13 -0
- package/cli/artifact-cli.mjs +605 -0
- package/cli/artifact-render.mjs +621 -0
- package/cli/artifact-server.mjs +847 -0
- package/cli/artifact.mjs +38 -2089
- package/cli/bg.mjs +5 -13
- package/cli/bin.mjs +170 -1348
- package/cli/commands/artifact.mjs +20 -0
- package/cli/commands/dash.mjs +160 -0
- package/cli/commands/headroom.mjs +204 -0
- package/cli/commands/install.mjs +169 -0
- package/cli/commands/memory.mjs +25 -0
- package/cli/commands/minimax.mjs +265 -0
- package/cli/commands/mod.mjs +185 -0
- package/cli/commands/service.mjs +65 -0
- package/cli/commands/usage.mjs +109 -0
- package/cli/commands/util.mjs +306 -0
- package/cli/doctor.mjs +6 -16
- package/cli/memory.mjs +65 -21
- package/cli/provision.mjs +10 -15
- package/cli/service-controller.mjs +1 -11
- package/cli/service.mjs +1 -11
- package/cli/utils.mjs +41 -1
- package/install.sh +1 -2
- package/package.json +8 -3
- package/bizar-dash/dist/assets/main-eWZ4NlCL.css +0 -1
- package/bizar-dash/dist/assets/main-usWhlPWa.js +0 -362
- package/bizar-dash/dist/assets/main-usWhlPWa.js.map +0 -1
- package/bizar-dash/dist/assets/mobile-O6ANdD4W.js.map +0 -1
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cli/commands/util.mjs
|
|
3
|
+
*
|
|
4
|
+
* Miscellaneous utility commands:
|
|
5
|
+
* audit, init, export, test-gate, dev-link, dev-unlink,
|
|
6
|
+
* doctor, repair, heads-up, bg, browser-harness-up, providers detect
|
|
7
|
+
*/
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
10
|
+
import { join } from 'node:path';
|
|
11
|
+
import { spawn } from 'node:child_process';
|
|
12
|
+
import { fileURLToPath } from 'node:url';
|
|
13
|
+
|
|
14
|
+
// ── Help texts ──────────────────────────────────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
export function showAuditHelp() {
|
|
17
|
+
console.log(`
|
|
18
|
+
bizar audit — Run security audit on agent configuration
|
|
19
|
+
|
|
20
|
+
Usage:
|
|
21
|
+
bizar audit
|
|
22
|
+
`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function showInitHelp() {
|
|
26
|
+
console.log(`
|
|
27
|
+
bizar init — Initialize .bizar/ in current project
|
|
28
|
+
|
|
29
|
+
Usage:
|
|
30
|
+
bizar init
|
|
31
|
+
|
|
32
|
+
Description:
|
|
33
|
+
Detects the project stack, creates .bizar/PROJECT.md and
|
|
34
|
+
.bizar/AGENTS_SELF_IMPROVEMENT.md and installs relevant skills.
|
|
35
|
+
The per-project knowledge graph (in .bizar/graph/) is provided
|
|
36
|
+
by the graphify mod — install it from the mod registry for
|
|
37
|
+
that feature.
|
|
38
|
+
`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function showExportHelp() {
|
|
42
|
+
console.log(`
|
|
43
|
+
bizar export — Export agents/rules to another harness
|
|
44
|
+
|
|
45
|
+
Usage:
|
|
46
|
+
bizar export [claude|cursor|opencode]
|
|
47
|
+
|
|
48
|
+
Description:
|
|
49
|
+
Copies installed Bizar agents and rules into another harness format.
|
|
50
|
+
`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function showTestGateHelp() {
|
|
54
|
+
console.log(`
|
|
55
|
+
bizar test-gate — Detect & run the project's test suite
|
|
56
|
+
`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function showDevLinkHelp() {
|
|
60
|
+
console.log(`
|
|
61
|
+
bizar dev-link / dev-unlink — Manage a symlink from the opencode plugin dir
|
|
62
|
+
to a local source checkout, so edits propagate to opencode on next session.
|
|
63
|
+
|
|
64
|
+
Usage:
|
|
65
|
+
bizar dev-link [source-dir] Symlink source-dir (default: ./plugins/bizar)
|
|
66
|
+
to ~/.config/opencode/plugins/bizar
|
|
67
|
+
bizar dev-link --force Replace an existing deployed copy
|
|
68
|
+
bizar dev-unlink Remove the dev symlink + restore from npm
|
|
69
|
+
bizar dev-unlink --force Remove even if not a symlink (destructive)
|
|
70
|
+
|
|
71
|
+
Description:
|
|
72
|
+
By default, opencode loads the Bizar plugin from
|
|
73
|
+
~/.config/opencode/plugins/bizar, which is a real directory copied
|
|
74
|
+
from the npm package. Edits to plugins/bizar/ in the BizarHarness
|
|
75
|
+
repo don't propagate until you re-run the installer.
|
|
76
|
+
|
|
77
|
+
\`bizar dev-link\` replaces that directory with a symlink pointing
|
|
78
|
+
at your local checkout, so source edits are picked up immediately.
|
|
79
|
+
\`bizar dev-unlink\` reverses the change by removing the symlink
|
|
80
|
+
and re-installing the deployed copy from the npm package.
|
|
81
|
+
|
|
82
|
+
While the dev link is in place, \`bizar update\` will skip the
|
|
83
|
+
plugin-copy step (and print a warning) so it doesn't clobber the
|
|
84
|
+
link. Run \`bizar dev-unlink\` first, or pass --force to overwrite.
|
|
85
|
+
|
|
86
|
+
Examples:
|
|
87
|
+
bizar dev-link
|
|
88
|
+
bizar dev-link /home/me/projects/bizar/plugins/bizar
|
|
89
|
+
bizar dev-link --force
|
|
90
|
+
bizar dev-unlink
|
|
91
|
+
`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function showDoctorHelp() {
|
|
95
|
+
console.log(`
|
|
96
|
+
bizar doctor — Check the BizarHarness install for health issues
|
|
97
|
+
|
|
98
|
+
Usage:
|
|
99
|
+
bizar doctor
|
|
100
|
+
|
|
101
|
+
Description:
|
|
102
|
+
Runs a battery of health checks against the local install:
|
|
103
|
+
• opencode CLI reachable
|
|
104
|
+
• ~/.config/opencode/opencode.json parses as JSON
|
|
105
|
+
• the Bizar plugin is registered
|
|
106
|
+
• plugin path resolves
|
|
107
|
+
• @polderlabs/bizar-plugin is installed globally
|
|
108
|
+
• core agent files are installed (odin, quick, thor, tyr)
|
|
109
|
+
• headroom / semble / skills on PATH (lenient — at least one)
|
|
110
|
+
• dashboard reachable (skipped if no port file)
|
|
111
|
+
• provider.minimax block + MiniMax model flags are sane
|
|
112
|
+
|
|
113
|
+
Prints ✓/✗ for each check and a final summary. Exits non-zero
|
|
114
|
+
if any check fails. Use \`bizar doctor\` after a manual config
|
|
115
|
+
edit or to diagnose "why is opencode misbehaving?" questions.
|
|
116
|
+
|
|
117
|
+
Related:
|
|
118
|
+
bizar update Update + auto-run doctor on success
|
|
119
|
+
`);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function showRepairHelp() {
|
|
123
|
+
console.log(`
|
|
124
|
+
bizar repair — Fix common install issues
|
|
125
|
+
|
|
126
|
+
Usage:
|
|
127
|
+
bizar repair Diagnose + fix stale bin symlinks and mismatched versions
|
|
128
|
+
bizar repair --dry-run Show what would change without modifying anything
|
|
129
|
+
bizar repair --bin-only Only fix the bin symlink; skip version checks
|
|
130
|
+
`);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ── Test gate ──────────────────────────────────────────────────────────────────
|
|
134
|
+
|
|
135
|
+
export async function runTestGate() {
|
|
136
|
+
console.log(chalk.bold.hex('#a855f7')('\n ᚦ TEST GATE ᚦ\n'));
|
|
137
|
+
const { execSync } = await import('node:child_process');
|
|
138
|
+
const cwd = process.cwd();
|
|
139
|
+
const possible = [
|
|
140
|
+
{ cmd: 'npm test', check: 'package.json' },
|
|
141
|
+
{ cmd: 'pytest', check: 'pyproject.toml' },
|
|
142
|
+
{ cmd: 'cargo test', check: 'Cargo.toml' },
|
|
143
|
+
{ cmd: 'go test ./...', check: 'go.mod' },
|
|
144
|
+
];
|
|
145
|
+
for (const suite of possible) {
|
|
146
|
+
try {
|
|
147
|
+
if (existsSync(join(cwd, suite.check))) {
|
|
148
|
+
console.log(` Running: ${suite.cmd}`);
|
|
149
|
+
execSync(suite.cmd, { stdio: 'inherit', timeout: 120000, cwd });
|
|
150
|
+
console.log('\n ✓ Test gate passed\n');
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
} catch {
|
|
154
|
+
console.log(`\n ✗ Test gate failed: ${suite.cmd}\n`);
|
|
155
|
+
process.exit(1);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
console.log(' No test suite detected. Install one to use the test gate.\n');
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// ── Main dispatcher ────────────────────────────────────────────────────────────
|
|
163
|
+
|
|
164
|
+
export async function run(name, args, isHelpRequest) {
|
|
165
|
+
switch (name) {
|
|
166
|
+
case 'audit':
|
|
167
|
+
if (isHelpRequest) showAuditHelp();
|
|
168
|
+
else {
|
|
169
|
+
const { runAudit } = await import('../audit.mjs');
|
|
170
|
+
await runAudit();
|
|
171
|
+
}
|
|
172
|
+
break;
|
|
173
|
+
|
|
174
|
+
case 'init':
|
|
175
|
+
if (isHelpRequest) showInitHelp();
|
|
176
|
+
else {
|
|
177
|
+
const { runInit } = await import('../init.mjs');
|
|
178
|
+
await runInit(process.cwd());
|
|
179
|
+
}
|
|
180
|
+
break;
|
|
181
|
+
|
|
182
|
+
case 'export':
|
|
183
|
+
if (isHelpRequest) showExportHelp();
|
|
184
|
+
else {
|
|
185
|
+
const { runExport } = await import('../export.mjs');
|
|
186
|
+
const targetFlag = args.includes('--target') ? args[args.indexOf('--target') + 1] : null;
|
|
187
|
+
await runExport(targetFlag);
|
|
188
|
+
}
|
|
189
|
+
break;
|
|
190
|
+
|
|
191
|
+
case 'test-gate':
|
|
192
|
+
if (isHelpRequest) showTestGateHelp();
|
|
193
|
+
else await runTestGate();
|
|
194
|
+
break;
|
|
195
|
+
|
|
196
|
+
case 'dev-link':
|
|
197
|
+
if (isHelpRequest) showDevLinkHelp();
|
|
198
|
+
else {
|
|
199
|
+
const { createDevLink } = await import('../dev-link.mjs');
|
|
200
|
+
const positional = args.filter((a) => !a.startsWith('-'));
|
|
201
|
+
const flags = args.filter((a) => a.startsWith('-'));
|
|
202
|
+
const sourceDir = positional[0] ?? null;
|
|
203
|
+
const force = flags.includes('--force') || flags.includes('-f');
|
|
204
|
+
const ok = createDevLink(sourceDir, { force });
|
|
205
|
+
if (!ok) process.exit(1);
|
|
206
|
+
}
|
|
207
|
+
break;
|
|
208
|
+
|
|
209
|
+
case 'dev-unlink':
|
|
210
|
+
if (isHelpRequest) showDevLinkHelp();
|
|
211
|
+
else {
|
|
212
|
+
const { removeDevLink } = await import('../dev-link.mjs');
|
|
213
|
+
const force = args.includes('--force') || args.includes('-f');
|
|
214
|
+
const ok = await removeDevLink({ force });
|
|
215
|
+
if (!ok) process.exit(1);
|
|
216
|
+
}
|
|
217
|
+
break;
|
|
218
|
+
|
|
219
|
+
case 'doctor':
|
|
220
|
+
if (isHelpRequest) showDoctorHelp();
|
|
221
|
+
else {
|
|
222
|
+
const { runDoctor } = await import('../doctor.mjs');
|
|
223
|
+
const wantJson = args.includes('--json');
|
|
224
|
+
const result = await runDoctor({ silent: wantJson, json: wantJson });
|
|
225
|
+
if (wantJson) process.stdout.write(JSON.stringify(result) + '\n');
|
|
226
|
+
if (result.failed > 0) process.exit(1);
|
|
227
|
+
}
|
|
228
|
+
break;
|
|
229
|
+
|
|
230
|
+
case 'repair':
|
|
231
|
+
if (isHelpRequest) showRepairHelp();
|
|
232
|
+
else {
|
|
233
|
+
const { runRepair } = await import('../repair.mjs');
|
|
234
|
+
const dryRun = args.includes('--dry-run');
|
|
235
|
+
const binOnly = args.includes('--bin-only');
|
|
236
|
+
const result = await runRepair({ dryRun, binOnly });
|
|
237
|
+
if (!result.ok) {
|
|
238
|
+
for (const n of result.notes) console.log(` ${n}`);
|
|
239
|
+
process.exit(1);
|
|
240
|
+
}
|
|
241
|
+
for (const n of result.notes) console.log(` ${n}`);
|
|
242
|
+
if (result.fixed.length > 0) {
|
|
243
|
+
console.log(chalk.green('\n Repair complete. Re-run your shell or `hash -r` to pick up the new path.'));
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
break;
|
|
247
|
+
|
|
248
|
+
case 'heads-up':
|
|
249
|
+
if (isHelpRequest) {
|
|
250
|
+
console.log(' heads-up <subcommand> Manage pre-push / pre-release heads-ups (list/check/archive)');
|
|
251
|
+
} else {
|
|
252
|
+
const { runHeadsUp } = await import('../heads-up.mjs');
|
|
253
|
+
await runHeadsUp(args[0], args.slice(1));
|
|
254
|
+
}
|
|
255
|
+
break;
|
|
256
|
+
|
|
257
|
+
case 'bg':
|
|
258
|
+
if (isHelpRequest) {
|
|
259
|
+
console.log(' bg <subcommand> Manage background agents (list/view/kill/logs)');
|
|
260
|
+
} else {
|
|
261
|
+
const { runBg } = await import('../bg.mjs');
|
|
262
|
+
await runBg(args[0], args.slice(1));
|
|
263
|
+
}
|
|
264
|
+
break;
|
|
265
|
+
|
|
266
|
+
case 'browser-harness-up': {
|
|
267
|
+
const { execFileSync } = await import('node:child_process');
|
|
268
|
+
const sub = args[0] || 'start';
|
|
269
|
+
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
|
270
|
+
const scriptPath = join(__dirname, '..', 'browser-harness-up.sh');
|
|
271
|
+
try {
|
|
272
|
+
const out = execFileSync('bash', [scriptPath, sub], {
|
|
273
|
+
encoding: 'utf8',
|
|
274
|
+
stdio: 'inherit',
|
|
275
|
+
});
|
|
276
|
+
if (out) process.stdout.write(out);
|
|
277
|
+
} catch (err) {
|
|
278
|
+
console.error(chalk.red(` ✗ browser-harness-up ${sub} failed (exit ${err.status ?? 1})`));
|
|
279
|
+
process.exit(err.status || 1);
|
|
280
|
+
}
|
|
281
|
+
break;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
case 'providers':
|
|
285
|
+
if (args[0] === 'detect') {
|
|
286
|
+
const { runProvidersDetect } = await import('../providers-detect.mjs');
|
|
287
|
+
await runProvidersDetect(args.slice(1));
|
|
288
|
+
}
|
|
289
|
+
break;
|
|
290
|
+
|
|
291
|
+
case 'plan':
|
|
292
|
+
// Alias for artifact
|
|
293
|
+
if (isHelpRequest) {
|
|
294
|
+
const { showHelp } = await import('../artifact-cli.mjs');
|
|
295
|
+
showHelp();
|
|
296
|
+
} else {
|
|
297
|
+
const { runArtifact } = await import('../artifact-cli.mjs');
|
|
298
|
+
await runArtifact(args, {});
|
|
299
|
+
}
|
|
300
|
+
break;
|
|
301
|
+
|
|
302
|
+
default:
|
|
303
|
+
return false;
|
|
304
|
+
}
|
|
305
|
+
return true;
|
|
306
|
+
}
|
package/cli/doctor.mjs
CHANGED
|
@@ -21,9 +21,8 @@
|
|
|
21
21
|
import chalk from 'chalk';
|
|
22
22
|
import { spawnSync } from 'node:child_process';
|
|
23
23
|
import { existsSync, readFileSync } from 'node:fs';
|
|
24
|
-
import { homedir } from 'node:os';
|
|
25
24
|
import { join } from 'node:path';
|
|
26
|
-
import { opencodeConfigDir, opencodeAgentsDir } from './utils.mjs';
|
|
25
|
+
import { opencodeConfigDir, opencodeAgentsDir, which, bizarConfigDir } from './utils.mjs';
|
|
27
26
|
|
|
28
27
|
// v3.20.11: list every agent the install script is expected to deploy.
|
|
29
28
|
// Adding a new agent to `config/agents/` without adding it here causes
|
|
@@ -50,12 +49,6 @@ const REQUIRED_AGENTS = [
|
|
|
50
49
|
|
|
51
50
|
// ── helpers ─────────────────────────────────────────────────────────────────
|
|
52
51
|
|
|
53
|
-
function bizarConfigDir() {
|
|
54
|
-
return process.env.XDG_CONFIG_HOME
|
|
55
|
-
? join(process.env.XDG_CONFIG_HOME, 'bizar')
|
|
56
|
-
: join(homedir(), '.config', 'bizar');
|
|
57
|
-
}
|
|
58
|
-
|
|
59
52
|
/**
|
|
60
53
|
* Run a check function and capture its result. The check either
|
|
61
54
|
* returns a string message (pass) or throws an Error (fail).
|
|
@@ -73,11 +66,6 @@ async function runCheck(name, fn) {
|
|
|
73
66
|
}
|
|
74
67
|
}
|
|
75
68
|
|
|
76
|
-
function which(cmd) {
|
|
77
|
-
const probe = spawnSync('which', [cmd], { stdio: 'ignore' });
|
|
78
|
-
return probe.status === 0;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
69
|
// ── individual checks ───────────────────────────────────────────────────────
|
|
82
70
|
|
|
83
71
|
async function checkOpencodeReachable() {
|
|
@@ -269,10 +257,12 @@ const CHECKS = [
|
|
|
269
257
|
|
|
270
258
|
/**
|
|
271
259
|
* Run all health checks. Prints per-check output unless `opts.silent`.
|
|
272
|
-
*
|
|
260
|
+
* Prints a final summary line unless `opts.json` is true.
|
|
261
|
+
* Returns `{ passed, failed, results }`.
|
|
273
262
|
*/
|
|
274
263
|
export async function runDoctor(opts = {}) {
|
|
275
264
|
const silent = !!opts.silent;
|
|
265
|
+
const jsonMode = !!opts.json;
|
|
276
266
|
const results = [];
|
|
277
267
|
for (const [name, fn] of CHECKS) {
|
|
278
268
|
const r = await runCheck(name, fn);
|
|
@@ -288,8 +278,8 @@ export async function runDoctor(opts = {}) {
|
|
|
288
278
|
const passed = results.filter((r) => r.ok).length;
|
|
289
279
|
const failed = results.length - passed;
|
|
290
280
|
|
|
291
|
-
//
|
|
292
|
-
if (failed > 0 || !silent) {
|
|
281
|
+
// Print summary line only when not in JSON mode
|
|
282
|
+
if (!jsonMode && (failed > 0 || !silent)) {
|
|
293
283
|
console.log('');
|
|
294
284
|
if (failed === 0) {
|
|
295
285
|
console.log(
|
package/cli/memory.mjs
CHANGED
|
@@ -755,7 +755,8 @@ function printSetupResult({ mode, remote, vaultPath }) {
|
|
|
755
755
|
/**
|
|
756
756
|
* `bizar memory status`
|
|
757
757
|
*/
|
|
758
|
-
async function cmdStatus(_args) {
|
|
758
|
+
async function cmdStatus(_args, opts = {}) {
|
|
759
|
+
const { wantJson = false } = opts;
|
|
759
760
|
const projectRoot = getProjectRoot();
|
|
760
761
|
const { loadConfig, resolveVault, initVault, listNotes } = memoryStore;
|
|
761
762
|
const { isGitInstalled, status: gitStatus } = memoryGit;
|
|
@@ -768,19 +769,15 @@ async function cmdStatus(_args) {
|
|
|
768
769
|
|
|
769
770
|
const { vaultRoot, mode, projectId, branch } = resolveVault(projectRoot);
|
|
770
771
|
|
|
771
|
-
|
|
772
|
-
console.log(' ─────────────────────────────');
|
|
773
|
-
kv('mode', mode);
|
|
774
|
-
kv('projectId', projectId);
|
|
775
|
-
kv('vault', vaultRoot);
|
|
776
|
-
kv('branch', branch);
|
|
772
|
+
const out = { mode, projectId, vault: vaultRoot, branch };
|
|
777
773
|
|
|
778
774
|
// Ensure vault exists
|
|
779
775
|
if (!existsSync(vaultRoot)) {
|
|
780
|
-
|
|
776
|
+
out.vaultExists = false;
|
|
781
777
|
} else {
|
|
778
|
+
out.vaultExists = true;
|
|
782
779
|
const notes = listNotes(projectRoot);
|
|
783
|
-
|
|
780
|
+
out.noteCount = notes.length;
|
|
784
781
|
}
|
|
785
782
|
|
|
786
783
|
// Git status if managed
|
|
@@ -788,14 +785,14 @@ async function cmdStatus(_args) {
|
|
|
788
785
|
if (isGitInstalled()) {
|
|
789
786
|
const gs = gitStatus(vaultRoot);
|
|
790
787
|
if (gs.ok) {
|
|
791
|
-
|
|
792
|
-
if (gs.ahead > 0)
|
|
793
|
-
if (gs.behind > 0)
|
|
788
|
+
out.git = { branch: gs.branch, clean: gs.clean };
|
|
789
|
+
if (gs.ahead > 0) out.git.ahead = gs.ahead;
|
|
790
|
+
if (gs.behind > 0) out.git.behind = gs.behind;
|
|
794
791
|
} else {
|
|
795
|
-
|
|
792
|
+
out.git = { error: 'not a git repo' };
|
|
796
793
|
}
|
|
797
794
|
} else {
|
|
798
|
-
|
|
795
|
+
out.git = { error: 'git not installed' };
|
|
799
796
|
}
|
|
800
797
|
}
|
|
801
798
|
|
|
@@ -804,15 +801,15 @@ async function cmdStatus(_args) {
|
|
|
804
801
|
const { isLightRAGRunning, isLightRAGInstalled, resolveLightRAGConfig } = memoryStore;
|
|
805
802
|
const cfg = resolveLightRAGConfig(projectRoot);
|
|
806
803
|
if (!cfg.enabled) {
|
|
807
|
-
|
|
804
|
+
out.lightrag = { enabled: false };
|
|
808
805
|
} else if (!(await isLightRAGInstalled())) {
|
|
809
|
-
|
|
806
|
+
out.lightrag = { enabled: true, installed: false };
|
|
810
807
|
} else {
|
|
811
808
|
const running = await isLightRAGRunning(cfg);
|
|
812
|
-
|
|
809
|
+
out.lightrag = { enabled: true, installed: true, running, host: cfg.host, port: cfg.port };
|
|
813
810
|
}
|
|
814
811
|
} catch (err) {
|
|
815
|
-
|
|
812
|
+
out.lightrag = { error: err.message };
|
|
816
813
|
}
|
|
817
814
|
|
|
818
815
|
// Last reindex attempt
|
|
@@ -820,10 +817,55 @@ async function cmdStatus(_args) {
|
|
|
820
817
|
if (existsSync(reindexMarker)) {
|
|
821
818
|
try {
|
|
822
819
|
const { finishedAt, ok, inserted, failed, noteCount } = JSON.parse(readFileSync(reindexMarker, 'utf8'));
|
|
823
|
-
|
|
820
|
+
out.lastReindex = { finishedAt, ok, inserted, failed, noteCount };
|
|
824
821
|
} catch { /* ignore */ }
|
|
825
822
|
}
|
|
826
823
|
|
|
824
|
+
if (wantJson) {
|
|
825
|
+
process.stdout.write(JSON.stringify(out) + '\n');
|
|
826
|
+
return;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
console.log(chalk.bold('\n Bizar Memory Status'));
|
|
830
|
+
console.log(' ─────────────────────────────');
|
|
831
|
+
kv('mode', mode);
|
|
832
|
+
kv('projectId', projectId);
|
|
833
|
+
kv('vault', vaultRoot);
|
|
834
|
+
kv('branch', branch);
|
|
835
|
+
if (!out.vaultExists) {
|
|
836
|
+
warn(`vault directory does not exist — run \`bizar memory init\` or \`bizar memory sync\``);
|
|
837
|
+
} else {
|
|
838
|
+
kv('noteCount', String(out.noteCount));
|
|
839
|
+
}
|
|
840
|
+
if (out.git) {
|
|
841
|
+
if (out.git.error) {
|
|
842
|
+
kv('git', chalk.yellow(out.git.error));
|
|
843
|
+
} else {
|
|
844
|
+
const cleanStr = out.git.clean ? chalk.green('clean') : chalk.red('dirty');
|
|
845
|
+
kv('git', `${out.git.branch} (${cleanStr})`);
|
|
846
|
+
if (out.git.ahead) kv('ahead', String(out.git.ahead));
|
|
847
|
+
if (out.git.behind) kv('behind', String(out.git.behind));
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
if (out.lightrag) {
|
|
851
|
+
if (out.lightrag.error) {
|
|
852
|
+
kv('lightrag', chalk.gray(`status unavailable (${out.lightrag.error})`));
|
|
853
|
+
} else if (!out.lightrag.enabled) {
|
|
854
|
+
kv('lightrag', chalk.gray('disabled'));
|
|
855
|
+
} else if (!out.lightrag.installed) {
|
|
856
|
+
kv('lightrag', chalk.yellow('not installed — `uv tool install "lightrag-hku[api]"`'));
|
|
857
|
+
} else {
|
|
858
|
+
const runningStr = out.lightrag.running
|
|
859
|
+
? chalk.green(`${out.lightrag.host}:${out.lightrag.port} (running)`)
|
|
860
|
+
: chalk.yellow(`${out.lightrag.host}:${out.lightrag.port} (not running)`);
|
|
861
|
+
kv('lightrag', runningStr);
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
if (out.lastReindex) {
|
|
865
|
+
const lr = out.lastReindex;
|
|
866
|
+
const lrStr = `${new Date(lr.finishedAt).toLocaleString()} — ${lr.ok ? chalk.green(`${lr.inserted}/${lr.noteCount} ok`) : chalk.red(`${lr.failed} failed`)}`;
|
|
867
|
+
kv('lastReindex', lrStr);
|
|
868
|
+
}
|
|
827
869
|
console.log();
|
|
828
870
|
}
|
|
829
871
|
|
|
@@ -1606,8 +1648,10 @@ async function cmdDelete(args) {
|
|
|
1606
1648
|
/**
|
|
1607
1649
|
* @param {string} subcommand
|
|
1608
1650
|
* @param {string[]} args
|
|
1651
|
+
* @param {{ wantJson?: boolean }} opts
|
|
1609
1652
|
*/
|
|
1610
|
-
export async function runMemory(subcommand, args) {
|
|
1653
|
+
export async function runMemory(subcommand, args, opts = {}) {
|
|
1654
|
+
const { wantJson = false } = opts;
|
|
1611
1655
|
switch (subcommand) {
|
|
1612
1656
|
case 'init':
|
|
1613
1657
|
await cmdInit(args);
|
|
@@ -1616,7 +1660,7 @@ export async function runMemory(subcommand, args) {
|
|
|
1616
1660
|
await cmdSetup(args);
|
|
1617
1661
|
break;
|
|
1618
1662
|
case 'status':
|
|
1619
|
-
await cmdStatus(args);
|
|
1663
|
+
await cmdStatus(args, { wantJson });
|
|
1620
1664
|
break;
|
|
1621
1665
|
case 'link':
|
|
1622
1666
|
await cmdLink(args);
|
package/cli/provision.mjs
CHANGED
|
@@ -42,9 +42,11 @@ import { existsSync, readFileSync, readdirSync, rmSync, writeFileSync, mkdirSync
|
|
|
42
42
|
import { homedir } from 'node:os';
|
|
43
43
|
import { join, dirname } from 'node:path';
|
|
44
44
|
import { fileURLToPath } from 'node:url';
|
|
45
|
+
import { bizarConfigDir } from './utils.mjs';
|
|
45
46
|
|
|
46
47
|
const __filename = fileURLToPath(import.meta.url);
|
|
47
48
|
const __dirname = dirname(__filename);
|
|
49
|
+
const HOME = homedir();
|
|
48
50
|
|
|
49
51
|
// `cli/provision.mjs` lives at `<pkg>/cli/provision.mjs`. The repo root
|
|
50
52
|
// (where `plugins/bizar/`, `bizar-dash/`, `package.json` etc. live) is
|
|
@@ -53,19 +55,6 @@ const __dirname = dirname(__filename);
|
|
|
53
55
|
export const REPO_ROOT = join(__dirname, '..');
|
|
54
56
|
export const PKG_MAIN = '@polderlabs/bizar';
|
|
55
57
|
|
|
56
|
-
const HOME = homedir();
|
|
57
|
-
|
|
58
|
-
function bizarConfigDir() {
|
|
59
|
-
if (process.platform === 'win32') {
|
|
60
|
-
return process.env.APPDATA
|
|
61
|
-
? join(process.env.APPDATA, 'bizar')
|
|
62
|
-
: join(HOME, '.config', 'bizar');
|
|
63
|
-
}
|
|
64
|
-
return process.env.XDG_CONFIG_HOME
|
|
65
|
-
? join(process.env.XDG_CONFIG_HOME, 'bizar')
|
|
66
|
-
: join(HOME, '.config', 'bizar');
|
|
67
|
-
}
|
|
68
|
-
|
|
69
58
|
export const BIZAR_HOME = bizarConfigDir();
|
|
70
59
|
export const OPENCODE_DIR =
|
|
71
60
|
process.platform === 'win32'
|
|
@@ -446,7 +435,10 @@ export async function ensureNpmPackage(pkg, { mode, dryRun, force }) {
|
|
|
446
435
|
// processes are reading files inside the npm-global install dir. We
|
|
447
436
|
// can't replace those files atomically while they're open. The caller
|
|
448
437
|
// is expected to have already killed them via ensureInstancesKilled().
|
|
449
|
-
const r = spawnSync('npm', ['install', '-g', `${pkg}@latest`], { stdio: 'inherit' });
|
|
438
|
+
const r = spawnSync('npm', ['install', '-g', `${pkg}@latest`], { stdio: 'inherit', timeout: 600000 });
|
|
439
|
+
if (r.status === null && r.error?.code === 'ETIMEDOUT') {
|
|
440
|
+
return { ok: false, message: `${pkg} install timed out after 10 minutes`, installed: current };
|
|
441
|
+
}
|
|
450
442
|
if (r.status !== 0) {
|
|
451
443
|
return { ok: false, message: `${pkg} install failed`, installed: current };
|
|
452
444
|
}
|
|
@@ -471,7 +463,10 @@ export async function updateOpencodeCli({ dryRun, force }) {
|
|
|
471
463
|
return { ok: true, message: 'opencode updated via `opencode upgrade`' };
|
|
472
464
|
}
|
|
473
465
|
console.log(chalk.dim(' opencode upgrade not available; falling back to npm'));
|
|
474
|
-
const r2 = spawnSync('npm', ['install', '-g', 'opencode-ai@latest'], { stdio: 'inherit' });
|
|
466
|
+
const r2 = spawnSync('npm', ['install', '-g', 'opencode-ai@latest'], { stdio: 'inherit', timeout: 600000 });
|
|
467
|
+
if (r2.status === null && r2.error?.code === 'ETIMEDOUT') {
|
|
468
|
+
return { ok: false, message: 'opencode install timed out after 10 minutes' };
|
|
469
|
+
}
|
|
475
470
|
if (r2.status === 0) {
|
|
476
471
|
return { ok: true, message: 'opencode updated via npm' };
|
|
477
472
|
}
|
|
@@ -39,6 +39,7 @@ import {
|
|
|
39
39
|
import { homedir, platform } from 'node:os';
|
|
40
40
|
import { join, dirname, resolve, isAbsolute } from 'node:path';
|
|
41
41
|
import { fileURLToPath } from 'node:url';
|
|
42
|
+
import { bizarConfigDir } from './utils.mjs';
|
|
42
43
|
|
|
43
44
|
const __filename = fileURLToPath(import.meta.url);
|
|
44
45
|
const __dirname = dirname(__filename);
|
|
@@ -47,17 +48,6 @@ const PLATFORM = platform();
|
|
|
47
48
|
|
|
48
49
|
// ── Path helpers ──────────────────────────────────────────────────────────────
|
|
49
50
|
|
|
50
|
-
function bizarConfigDir() {
|
|
51
|
-
if (PLATFORM === 'win32') {
|
|
52
|
-
return process.env.APPDATA
|
|
53
|
-
? join(process.env.APPDATA, 'bizar')
|
|
54
|
-
: join(HOME, '.config', 'bizar');
|
|
55
|
-
}
|
|
56
|
-
return process.env.XDG_CONFIG_HOME
|
|
57
|
-
? join(process.env.XDG_CONFIG_HOME, 'bizar')
|
|
58
|
-
: join(HOME, '.config', 'bizar');
|
|
59
|
-
}
|
|
60
|
-
|
|
61
51
|
function userUnitDir() {
|
|
62
52
|
if (PLATFORM === 'win32') return null;
|
|
63
53
|
return process.env.XDG_CONFIG_HOME
|
package/cli/service.mjs
CHANGED
|
@@ -35,22 +35,12 @@ import { join, dirname, resolve } from 'node:path';
|
|
|
35
35
|
import { homedir } from 'node:os';
|
|
36
36
|
import { fileURLToPath } from 'node:url';
|
|
37
37
|
import { spawn } from 'node:child_process';
|
|
38
|
+
import { bizarConfigDir } from './utils.mjs';
|
|
38
39
|
|
|
39
40
|
const __filename = fileURLToPath(import.meta.url);
|
|
40
41
|
const __dirname = dirname(__filename);
|
|
41
42
|
const HOME = homedir();
|
|
42
43
|
|
|
43
|
-
function bizarConfigDir() {
|
|
44
|
-
if (process.platform === 'win32') {
|
|
45
|
-
return process.env.APPDATA
|
|
46
|
-
? join(process.env.APPDATA, 'bizar')
|
|
47
|
-
: join(HOME, '.config', 'bizar');
|
|
48
|
-
}
|
|
49
|
-
return process.env.XDG_CONFIG_HOME
|
|
50
|
-
? join(process.env.XDG_CONFIG_HOME, 'bizar')
|
|
51
|
-
: join(HOME, '.config', 'bizar');
|
|
52
|
-
}
|
|
53
|
-
|
|
54
44
|
const BIZAR_HOME = bizarConfigDir();
|
|
55
45
|
const LOG_FILE = join(BIZAR_HOME, 'service.log');
|
|
56
46
|
const PID_FILE = join(BIZAR_HOME, 'service.pid');
|
package/cli/utils.mjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { access, constants, readFile } from 'node:fs/promises';
|
|
2
|
-
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { spawnSync, execFileSync } from 'node:child_process';
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
6
7
|
|
|
7
8
|
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
|
8
9
|
const REPO_ROOT = join(__dirname, '..');
|
|
@@ -153,3 +154,42 @@ export function buildSummary(components, agents, target, skillPacks = []) {
|
|
|
153
154
|
parts,
|
|
154
155
|
};
|
|
155
156
|
}
|
|
157
|
+
|
|
158
|
+
// ── Path helpers ─────────────────────────────────────────────────────────────────
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Return the platform-specific Bizar config directory.
|
|
162
|
+
* Windows: %APPDATA%\bizar
|
|
163
|
+
* Unix: $XDG_CONFIG_HOME/bizar (default: ~/.config/bizar)
|
|
164
|
+
*/
|
|
165
|
+
export function bizarConfigDir() {
|
|
166
|
+
if (process.platform === 'win32') {
|
|
167
|
+
return process.env.APPDATA
|
|
168
|
+
? join(process.env.APPDATA, 'bizar')
|
|
169
|
+
: join(homedir(), '.config', 'bizar');
|
|
170
|
+
}
|
|
171
|
+
return process.env.XDG_CONFIG_HOME
|
|
172
|
+
? join(process.env.XDG_CONFIG_HOME, 'bizar')
|
|
173
|
+
: join(homedir(), '.config', 'bizar');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Check if a command exists on PATH.
|
|
178
|
+
* Returns true if found, false otherwise.
|
|
179
|
+
*/
|
|
180
|
+
export function which(cmd) {
|
|
181
|
+
const probe = spawnSync('which', [cmd], { stdio: 'ignore' });
|
|
182
|
+
return probe.status === 0;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Check if a command exists on PATH (bg.mjs variant that returns the path or null).
|
|
187
|
+
*/
|
|
188
|
+
export function whichPath(cmd) {
|
|
189
|
+
try {
|
|
190
|
+
const out = execFileSync('which', [cmd], { encoding: 'utf8' });
|
|
191
|
+
return out.trim() || null;
|
|
192
|
+
} catch {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
}
|