brainctl 0.1.16 → 0.1.18
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/cli.d.ts +4 -6
- package/dist/cli.js +11 -16
- package/dist/commands/profile.d.ts +4 -0
- package/dist/commands/profile.js +106 -16
- package/dist/commands/status.js +7 -7
- package/dist/mcp/server.d.ts +5 -0
- package/dist/mcp/server.js +85 -154
- package/dist/services/agent-asset-installer.d.ts +3 -0
- package/dist/services/agent-asset-installer.js +109 -0
- package/dist/services/agent-availability-service.d.ts +11 -0
- package/dist/services/agent-availability-service.js +32 -0
- package/dist/services/credential-redaction-service.d.ts +1 -0
- package/dist/services/credential-redaction-service.js +9 -3
- package/dist/services/doctor-service.d.ts +2 -2
- package/dist/services/doctor-service.js +7 -63
- package/dist/services/portable-profile-pack-service.d.ts +6 -0
- package/dist/services/portable-profile-pack-service.js +78 -4
- package/dist/services/profile-apply-service.d.ts +34 -0
- package/dist/services/profile-apply-service.js +102 -0
- package/dist/services/profile-export-service.d.ts +5 -1
- package/dist/services/profile-export-service.js +3 -1
- package/dist/services/profile-import-service.js +82 -127
- package/dist/services/profile-service.d.ts +3 -11
- package/dist/services/profile-service.js +57 -102
- package/dist/services/profile-snapshot-service.d.ts +12 -0
- package/dist/services/profile-snapshot-service.js +47 -0
- package/dist/services/status-service.d.ts +9 -7
- package/dist/services/status-service.js +14 -13
- package/dist/types.d.ts +2 -57
- package/dist/ui/routes.d.ts +0 -2
- package/dist/ui/routes.js +71 -120
- package/dist/web/assets/index-CGmTbSgk.js +63 -0
- package/dist/web/assets/index-EIVU5Woh.css +2 -0
- package/dist/web/brainctl-mark.svg +13 -0
- package/dist/web/index.html +2 -5
- package/package.json +2 -1
- package/dist/commands/init.d.ts +0 -3
- package/dist/commands/init.js +0 -27
- package/dist/commands/run.d.ts +0 -3
- package/dist/commands/run.js +0 -25
- package/dist/commands/sync.d.ts +0 -3
- package/dist/commands/sync.js +0 -31
- package/dist/config.d.ts +0 -14
- package/dist/config.js +0 -96
- package/dist/context/builder.d.ts +0 -6
- package/dist/context/builder.js +0 -13
- package/dist/context/memory.d.ts +0 -5
- package/dist/context/memory.js +0 -43
- package/dist/context/skills.d.ts +0 -2
- package/dist/context/skills.js +0 -8
- package/dist/executor/claude.d.ts +0 -12
- package/dist/executor/claude.js +0 -16
- package/dist/executor/codex.d.ts +0 -12
- package/dist/executor/codex.js +0 -16
- package/dist/executor/process.d.ts +0 -11
- package/dist/executor/process.js +0 -40
- package/dist/executor/resolver.d.ts +0 -13
- package/dist/executor/resolver.js +0 -60
- package/dist/executor/types.d.ts +0 -14
- package/dist/executor/types.js +0 -1
- package/dist/services/config-write-service.d.ts +0 -12
- package/dist/services/config-write-service.js +0 -70
- package/dist/services/init-service.d.ts +0 -14
- package/dist/services/init-service.js +0 -88
- package/dist/services/memory-write-service.d.ts +0 -12
- package/dist/services/memory-write-service.js +0 -56
- package/dist/services/run-service.d.ts +0 -15
- package/dist/services/run-service.js +0 -94
- package/dist/services/sync-service.d.ts +0 -15
- package/dist/services/sync-service.js +0 -69
- package/dist/ui/streaming.d.ts +0 -3
- package/dist/ui/streaming.js +0 -16
- package/dist/web/assets/index-CuNIAQ7N.js +0 -65
- package/dist/web/assets/index-Ow6x3bQk.css +0 -2
package/dist/types.d.ts
CHANGED
|
@@ -1,55 +1,6 @@
|
|
|
1
1
|
export type AgentName = 'claude' | 'codex' | 'gemini';
|
|
2
2
|
export type ErrorCategory = 'user' | 'system';
|
|
3
3
|
export type DiagnosticStatus = 'ok' | 'warn' | 'error';
|
|
4
|
-
export interface SkillConfig {
|
|
5
|
-
description?: string;
|
|
6
|
-
prompt: string;
|
|
7
|
-
}
|
|
8
|
-
export interface BrainctlConfig {
|
|
9
|
-
configPath: string;
|
|
10
|
-
rootDir: string;
|
|
11
|
-
memory: {
|
|
12
|
-
paths: string[];
|
|
13
|
-
};
|
|
14
|
-
skills: Record<string, SkillConfig>;
|
|
15
|
-
mcps: Record<string, unknown>;
|
|
16
|
-
}
|
|
17
|
-
export interface MemoryLoadResult {
|
|
18
|
-
content: string;
|
|
19
|
-
files: string[];
|
|
20
|
-
count: number;
|
|
21
|
-
entries: Array<{
|
|
22
|
-
path: string;
|
|
23
|
-
content: string;
|
|
24
|
-
}>;
|
|
25
|
-
}
|
|
26
|
-
export interface RunRequest {
|
|
27
|
-
cwd?: string;
|
|
28
|
-
skill: string;
|
|
29
|
-
inputFile: string;
|
|
30
|
-
primaryAgent: AgentName;
|
|
31
|
-
fallbackAgent?: AgentName;
|
|
32
|
-
}
|
|
33
|
-
export interface ExecutionStep {
|
|
34
|
-
skill: string;
|
|
35
|
-
inputFile: string;
|
|
36
|
-
primaryAgent: AgentName;
|
|
37
|
-
fallbackAgent?: AgentName;
|
|
38
|
-
usePreviousOutput?: boolean;
|
|
39
|
-
}
|
|
40
|
-
export interface ExecutionStepResult {
|
|
41
|
-
stepIndex: number;
|
|
42
|
-
requestedAgent: AgentName;
|
|
43
|
-
agent: AgentName;
|
|
44
|
-
fallbackUsed: boolean;
|
|
45
|
-
exitCode: number;
|
|
46
|
-
output: string;
|
|
47
|
-
}
|
|
48
|
-
export interface ExecutionTrace {
|
|
49
|
-
steps: ExecutionStepResult[];
|
|
50
|
-
finalOutput: string;
|
|
51
|
-
finalExitCode: number;
|
|
52
|
-
}
|
|
53
4
|
export interface DiagnosticCheck {
|
|
54
5
|
label: string;
|
|
55
6
|
status: DiagnosticStatus;
|
|
@@ -129,20 +80,14 @@ export type McpServerConfig = LocalMcpServerConfig | RemoteMcpServerConfig;
|
|
|
129
80
|
export interface ProfileConfig {
|
|
130
81
|
name: string;
|
|
131
82
|
description?: string;
|
|
132
|
-
skills: Record<string, SkillConfig>;
|
|
133
83
|
mcps: Record<string, McpServerConfig>;
|
|
134
|
-
memory: {
|
|
135
|
-
paths: string[];
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
export interface BrainctlMetaConfig {
|
|
139
|
-
active_profile: string;
|
|
140
|
-
agents: AgentName[];
|
|
141
84
|
}
|
|
142
85
|
export interface SyncAgentResult {
|
|
143
86
|
agent: AgentName;
|
|
144
87
|
configPath: string;
|
|
145
88
|
backedUpTo: string | null;
|
|
146
89
|
mcpCount: number;
|
|
90
|
+
pluginsInstalled?: string[];
|
|
91
|
+
userSkillsInstalled?: string[];
|
|
147
92
|
}
|
|
148
93
|
export type SyncResult = SyncAgentResult[];
|
package/dist/ui/routes.d.ts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
-
import type { RunService } from '../services/run-service.js';
|
|
3
2
|
import type { StatusService } from '../services/status-service.js';
|
|
4
3
|
export interface UiRouteDependencies {
|
|
5
4
|
cwd: string;
|
|
6
5
|
statusService?: StatusService;
|
|
7
|
-
runService?: RunService;
|
|
8
6
|
}
|
|
9
7
|
export type UiRouteHandler = (request: IncomingMessage, response: ServerResponse) => Promise<void>;
|
|
10
8
|
export declare function createUiRouteHandler(dependencies: UiRouteDependencies): UiRouteHandler;
|
package/dist/ui/routes.js
CHANGED
|
@@ -1,32 +1,26 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
import { readFile } from 'node:fs/promises';
|
|
3
|
-
import { loadConfig } from '../config.js';
|
|
4
|
-
import { parseConfigPayload } from '../config.js';
|
|
5
3
|
import { BrainctlError, ProfileError, ProfileNotFoundError, ValidationError } from '../errors.js';
|
|
6
|
-
import { loadMemory } from '../context/memory.js';
|
|
7
4
|
import { createAgentConfigService } from '../services/agent-config-service.js';
|
|
8
|
-
import { createConfigWriteService } from '../services/config-write-service.js';
|
|
9
5
|
import { createMcpPreflightService } from '../services/mcp-preflight-service.js';
|
|
10
6
|
import { createPluginInstallService } from '../services/plugin-install-service.js';
|
|
11
7
|
import { createProfileExportService } from '../services/profile-export-service.js';
|
|
12
8
|
import { createProfileImportService } from '../services/profile-import-service.js';
|
|
13
9
|
import { createProfileService } from '../services/profile-service.js';
|
|
14
|
-
import { createRunService } from '../services/run-service.js';
|
|
15
10
|
import { createSkillPreflightService } from '../services/skill-preflight-service.js';
|
|
16
11
|
import { createStatusService } from '../services/status-service.js';
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
12
|
+
import { createProfileApplyService, } from '../services/profile-apply-service.js';
|
|
13
|
+
import { createProfileSnapshotService, defaultBackupProfileName, } from '../services/profile-snapshot-service.js';
|
|
19
14
|
import path from 'node:path';
|
|
20
15
|
import { fileURLToPath } from 'node:url';
|
|
21
16
|
const uiAssetRoot = resolveUiAssetRoot();
|
|
22
17
|
export function createUiRouteHandler(dependencies) {
|
|
23
18
|
const statusService = dependencies.statusService ?? createStatusService();
|
|
24
|
-
const runService = dependencies.runService ?? createRunService();
|
|
25
|
-
const configWriteService = createConfigWriteService();
|
|
26
19
|
const profileService = createProfileService();
|
|
27
20
|
const profileExportService = createProfileExportService({ profileService });
|
|
28
21
|
const profileImportService = createProfileImportService();
|
|
29
|
-
const
|
|
22
|
+
const profileApplyService = createProfileApplyService({ profileService });
|
|
23
|
+
const profileSnapshotService = createProfileSnapshotService();
|
|
30
24
|
const agentConfigService = createAgentConfigService();
|
|
31
25
|
const mcpPreflightService = createMcpPreflightService();
|
|
32
26
|
const pluginInstallService = createPluginInstallService();
|
|
@@ -41,71 +35,6 @@ export function createUiRouteHandler(dependencies) {
|
|
|
41
35
|
const overview = await statusService.execute({ cwd: dependencies.cwd });
|
|
42
36
|
return sendJson(response, 200, overview);
|
|
43
37
|
}
|
|
44
|
-
case '/api/run/stream': {
|
|
45
|
-
if (request.method !== 'GET') {
|
|
46
|
-
return sendJson(response, 405, { error: 'Method not allowed' });
|
|
47
|
-
}
|
|
48
|
-
const runRequest = parseRunRequest(url);
|
|
49
|
-
if (runRequest === null) {
|
|
50
|
-
return sendJson(response, 400, {
|
|
51
|
-
error: 'Missing skill, inputFile, or primaryAgent'
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
if ('error' in runRequest) {
|
|
55
|
-
return sendJson(response, 400, {
|
|
56
|
-
error: runRequest.error
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
startSseStream(response);
|
|
60
|
-
try {
|
|
61
|
-
const trace = await runService.execute({
|
|
62
|
-
...runRequest.request,
|
|
63
|
-
cwd: dependencies.cwd
|
|
64
|
-
}, {
|
|
65
|
-
onOutputChunk: (chunk) => {
|
|
66
|
-
writeSseEvent(response, 'output', chunk);
|
|
67
|
-
},
|
|
68
|
-
streamOutput: false
|
|
69
|
-
});
|
|
70
|
-
writeSseEvent(response, 'result', trace);
|
|
71
|
-
response.end();
|
|
72
|
-
}
|
|
73
|
-
catch (error) {
|
|
74
|
-
writeSseEvent(response, 'run-error', {
|
|
75
|
-
error: error instanceof Error ? error.message : 'Unexpected server error'
|
|
76
|
-
});
|
|
77
|
-
response.end();
|
|
78
|
-
}
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
case '/api/config': {
|
|
82
|
-
if (request.method === 'PUT') {
|
|
83
|
-
const body = await readJsonBody(request);
|
|
84
|
-
if (!body.ok) {
|
|
85
|
-
return sendJson(response, 400, { error: 'Invalid JSON body' });
|
|
86
|
-
}
|
|
87
|
-
const config = parseConfigPayload(body.value);
|
|
88
|
-
await configWriteService.execute({
|
|
89
|
-
cwd: dependencies.cwd,
|
|
90
|
-
config
|
|
91
|
-
});
|
|
92
|
-
const savedConfig = await loadConfig({ cwd: dependencies.cwd });
|
|
93
|
-
return sendJson(response, 200, savedConfig);
|
|
94
|
-
}
|
|
95
|
-
if (request.method !== 'GET') {
|
|
96
|
-
return sendJson(response, 405, { error: 'Method not allowed' });
|
|
97
|
-
}
|
|
98
|
-
const config = await loadConfig({ cwd: dependencies.cwd });
|
|
99
|
-
return sendJson(response, 200, config);
|
|
100
|
-
}
|
|
101
|
-
case '/api/memory': {
|
|
102
|
-
if (request.method !== 'GET') {
|
|
103
|
-
return sendJson(response, 405, { error: 'Method not allowed' });
|
|
104
|
-
}
|
|
105
|
-
const config = await loadConfig({ cwd: dependencies.cwd });
|
|
106
|
-
const memory = await loadMemory({ paths: config.memory.paths });
|
|
107
|
-
return sendJson(response, 200, memory);
|
|
108
|
-
}
|
|
109
38
|
case '/api/agents': {
|
|
110
39
|
if (request.method !== 'GET') {
|
|
111
40
|
return sendJson(response, 405, { error: 'Method not allowed' });
|
|
@@ -207,21 +136,58 @@ export function createUiRouteHandler(dependencies) {
|
|
|
207
136
|
return sendProfileError(response, error);
|
|
208
137
|
}
|
|
209
138
|
}
|
|
210
|
-
case '/api/
|
|
139
|
+
case '/api/profiles/snapshot': {
|
|
211
140
|
if (request.method !== 'POST') {
|
|
212
141
|
return sendJson(response, 405, { error: 'Method not allowed' });
|
|
213
142
|
}
|
|
143
|
+
const body = await readJsonBody(request);
|
|
144
|
+
if (!body.ok) {
|
|
145
|
+
return sendJson(response, 400, { error: 'Invalid JSON body' });
|
|
146
|
+
}
|
|
147
|
+
const data = (body.value ?? {});
|
|
148
|
+
if (data.agent !== 'claude' && data.agent !== 'codex' && data.agent !== 'gemini') {
|
|
149
|
+
return sendJson(response, 400, { error: 'Invalid agent' });
|
|
150
|
+
}
|
|
151
|
+
const profileName = data.as ?? defaultBackupProfileName(data.agent);
|
|
214
152
|
try {
|
|
215
|
-
const result = await
|
|
216
|
-
|
|
153
|
+
const result = await profileSnapshotService.execute({
|
|
154
|
+
cwd: dependencies.cwd,
|
|
155
|
+
agent: data.agent,
|
|
156
|
+
profileName,
|
|
157
|
+
});
|
|
158
|
+
return sendJson(response, 200, { profileName, ...result });
|
|
217
159
|
}
|
|
218
160
|
catch (error) {
|
|
219
|
-
return
|
|
220
|
-
error: error instanceof Error ? error.message : 'Sync failed',
|
|
221
|
-
});
|
|
161
|
+
return sendProfileError(response, error);
|
|
222
162
|
}
|
|
223
163
|
}
|
|
224
164
|
default: {
|
|
165
|
+
// Profile apply: POST /api/profiles/:name/apply
|
|
166
|
+
const applyMatch = url.pathname.match(/^\/api\/profiles\/([^/]+)\/apply$/);
|
|
167
|
+
if (applyMatch) {
|
|
168
|
+
if (request.method !== 'POST') {
|
|
169
|
+
return sendJson(response, 405, { error: 'Method not allowed' });
|
|
170
|
+
}
|
|
171
|
+
const profileName = decodeURIComponent(applyMatch[1]);
|
|
172
|
+
const body = await readJsonBody(request);
|
|
173
|
+
if (!body.ok) {
|
|
174
|
+
return sendJson(response, 400, { error: 'Invalid JSON body' });
|
|
175
|
+
}
|
|
176
|
+
const data = (body.value ?? {});
|
|
177
|
+
try {
|
|
178
|
+
const result = await profileApplyService.execute({
|
|
179
|
+
cwd: dependencies.cwd,
|
|
180
|
+
profileName,
|
|
181
|
+
agents: data.agents ?? ['claude', 'codex', 'gemini'],
|
|
182
|
+
items: data.items,
|
|
183
|
+
backup: data.backup,
|
|
184
|
+
});
|
|
185
|
+
return sendJson(response, 200, result);
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
return sendProfileError(response, error);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
225
191
|
// Agent MCP routes: /api/agents/:name/mcps(/:key)
|
|
226
192
|
const agentMcpCheckMatch = url.pathname.match(/^\/api\/agents\/(claude|codex|gemini)\/mcps\/check$/);
|
|
227
193
|
if (agentMcpCheckMatch) {
|
|
@@ -450,22 +416,34 @@ export function createUiRouteHandler(dependencies) {
|
|
|
450
416
|
}
|
|
451
417
|
return sendJson(response, 405, { error: 'Method not allowed' });
|
|
452
418
|
}
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
419
|
+
// Profile contents: GET /api/profiles/:name/contents
|
|
420
|
+
const contentsMatch = url.pathname.match(/^\/api\/profiles\/([^/]+)\/contents$/);
|
|
421
|
+
if (contentsMatch) {
|
|
422
|
+
if (request.method !== 'GET') {
|
|
423
|
+
return sendJson(response, 405, { error: 'Method not allowed' });
|
|
424
|
+
}
|
|
425
|
+
const profileName = decodeURIComponent(contentsMatch[1]);
|
|
426
|
+
try {
|
|
427
|
+
const profile = await profileService.get({ cwd: dependencies.cwd, name: profileName });
|
|
428
|
+
const manifestPath = path.join(dependencies.cwd, '.brainctl', 'profiles', profileName, 'manifest.yaml');
|
|
429
|
+
let manifest = null;
|
|
461
430
|
try {
|
|
462
|
-
const
|
|
463
|
-
|
|
431
|
+
const { readFile: readManifest } = await import('node:fs/promises');
|
|
432
|
+
const yamlMod = await import('yaml');
|
|
433
|
+
manifest = yamlMod.default.parse(await readManifest(manifestPath, 'utf8'));
|
|
464
434
|
}
|
|
465
|
-
catch
|
|
466
|
-
|
|
435
|
+
catch {
|
|
436
|
+
manifest = null;
|
|
467
437
|
}
|
|
438
|
+
return sendJson(response, 200, { profile, manifest });
|
|
468
439
|
}
|
|
440
|
+
catch (error) {
|
|
441
|
+
return sendProfileError(response, error);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
const profileMatch = url.pathname.match(/^\/api\/profiles\/([^/]+)$/);
|
|
445
|
+
if (profileMatch) {
|
|
446
|
+
const name = decodeURIComponent(profileMatch[1]);
|
|
469
447
|
if (request.method === 'GET') {
|
|
470
448
|
try {
|
|
471
449
|
const profile = await profileService.get({ cwd: dependencies.cwd, name });
|
|
@@ -548,33 +526,6 @@ async function readJsonBody(request) {
|
|
|
548
526
|
return { ok: false };
|
|
549
527
|
}
|
|
550
528
|
}
|
|
551
|
-
function parseRunRequest(url) {
|
|
552
|
-
const skill = url.searchParams.get('skill');
|
|
553
|
-
const inputFile = url.searchParams.get('inputFile');
|
|
554
|
-
const primaryAgent = parseAgentName(url.searchParams.get('primaryAgent'));
|
|
555
|
-
const fallbackAgentParam = url.searchParams.get('fallbackAgent');
|
|
556
|
-
const fallbackAgent = fallbackAgentParam === null ? null : parseAgentName(fallbackAgentParam);
|
|
557
|
-
if (!skill || !inputFile || !primaryAgent || fallbackAgentParam !== null && !fallbackAgent) {
|
|
558
|
-
return null;
|
|
559
|
-
}
|
|
560
|
-
if (fallbackAgent !== null && fallbackAgent === primaryAgent) {
|
|
561
|
-
return { error: 'fallbackAgent must differ from primaryAgent' };
|
|
562
|
-
}
|
|
563
|
-
return {
|
|
564
|
-
request: {
|
|
565
|
-
skill,
|
|
566
|
-
inputFile,
|
|
567
|
-
primaryAgent,
|
|
568
|
-
fallbackAgent: fallbackAgent ?? undefined
|
|
569
|
-
}
|
|
570
|
-
};
|
|
571
|
-
}
|
|
572
|
-
function parseAgentName(value) {
|
|
573
|
-
if (value === 'claude' || value === 'codex') {
|
|
574
|
-
return value;
|
|
575
|
-
}
|
|
576
|
-
return null;
|
|
577
|
-
}
|
|
578
529
|
function sendProfileError(response, error) {
|
|
579
530
|
if (error instanceof ProfileNotFoundError) {
|
|
580
531
|
return sendJson(response, 404, { error: error.message });
|