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/cli.d.ts
CHANGED
|
@@ -1,22 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from 'commander';
|
|
3
3
|
import { type DoctorService } from './services/doctor-service.js';
|
|
4
|
-
import { type
|
|
4
|
+
import { type ProfileApplyService } from './services/profile-apply-service.js';
|
|
5
5
|
import { type ProfileExportService } from './services/profile-export-service.js';
|
|
6
6
|
import { type ProfileImportService } from './services/profile-import-service.js';
|
|
7
7
|
import { type ProfileService } from './services/profile-service.js';
|
|
8
|
-
import { type
|
|
8
|
+
import { type ProfileSnapshotService } from './services/profile-snapshot-service.js';
|
|
9
9
|
import { type StatusService } from './services/status-service.js';
|
|
10
|
-
import { type SyncService } from './services/sync-service.js';
|
|
11
10
|
export interface CliServices {
|
|
12
|
-
initService: InitService;
|
|
13
|
-
runService: RunService;
|
|
14
11
|
statusService: StatusService;
|
|
15
12
|
doctorService: DoctorService;
|
|
16
13
|
profileService: ProfileService;
|
|
17
14
|
profileExportService: ProfileExportService;
|
|
18
15
|
profileImportService: ProfileImportService;
|
|
19
|
-
|
|
16
|
+
profileApplyService: ProfileApplyService;
|
|
17
|
+
profileSnapshotService: ProfileSnapshotService;
|
|
20
18
|
}
|
|
21
19
|
export declare function createProgram(overrides?: Partial<CliServices>): Command;
|
|
22
20
|
export declare function main(argv?: string[]): Promise<void>;
|
package/dist/cli.js
CHANGED
|
@@ -5,24 +5,20 @@ import { createInterface } from 'node:readline';
|
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
6
|
import { Command } from 'commander';
|
|
7
7
|
import { registerDoctorCommand } from './commands/doctor.js';
|
|
8
|
-
import { registerInitCommand } from './commands/init.js';
|
|
9
8
|
import { registerMcpCommand } from './commands/mcp.js';
|
|
10
9
|
import { registerProfileCommand } from './commands/profile.js';
|
|
11
|
-
import { registerRunCommand } from './commands/run.js';
|
|
12
10
|
import { registerStatusCommand } from './commands/status.js';
|
|
13
|
-
import { registerSyncCommand } from './commands/sync.js';
|
|
14
11
|
import { registerUiCommand } from './commands/ui.js';
|
|
15
12
|
import { printError } from './output.js';
|
|
16
13
|
import { createUpdateCheckService } from './services/update-check-service.js';
|
|
17
14
|
import { createDoctorService } from './services/doctor-service.js';
|
|
18
|
-
import {
|
|
15
|
+
import { createProfileApplyService } from './services/profile-apply-service.js';
|
|
19
16
|
import { createProfileExportService } from './services/profile-export-service.js';
|
|
20
17
|
import { createProfileImportService } from './services/profile-import-service.js';
|
|
21
18
|
import { createProfileService } from './services/profile-service.js';
|
|
22
|
-
import {
|
|
19
|
+
import { createProfileSnapshotService } from './services/profile-snapshot-service.js';
|
|
23
20
|
import { createStatusService } from './services/status-service.js';
|
|
24
|
-
import {
|
|
25
|
-
import { createExecutorResolver } from './executor/resolver.js';
|
|
21
|
+
import { createAgentAvailabilityService } from './services/agent-availability-service.js';
|
|
26
22
|
const packageVersion = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
|
|
27
23
|
export function createProgram(overrides = {}) {
|
|
28
24
|
const services = createDefaultServices(overrides);
|
|
@@ -31,16 +27,15 @@ export function createProgram(overrides = {}) {
|
|
|
31
27
|
.name('brainctl')
|
|
32
28
|
.description('Manage repeatable AI environments for local agent workflows')
|
|
33
29
|
.version(packageVersion.version);
|
|
34
|
-
registerInitCommand(program, services.initService);
|
|
35
30
|
registerStatusCommand(program, services.statusService);
|
|
36
|
-
registerRunCommand(program, services.runService);
|
|
37
31
|
registerDoctorCommand(program, services.doctorService);
|
|
38
32
|
registerProfileCommand(program, {
|
|
39
33
|
profileService: services.profileService,
|
|
40
34
|
profileExportService: services.profileExportService,
|
|
41
35
|
profileImportService: services.profileImportService,
|
|
36
|
+
profileApplyService: services.profileApplyService,
|
|
37
|
+
profileSnapshotService: services.profileSnapshotService,
|
|
42
38
|
});
|
|
43
|
-
registerSyncCommand(program, services.syncService);
|
|
44
39
|
registerUiCommand(program);
|
|
45
40
|
registerMcpCommand(program);
|
|
46
41
|
return program;
|
|
@@ -100,17 +95,17 @@ export function shouldRunMain(entryPointPath, moduleUrl) {
|
|
|
100
95
|
return resolveRealPath(entryPointPath) === resolveRealPath(fileURLToPath(moduleUrl));
|
|
101
96
|
}
|
|
102
97
|
function createDefaultServices(overrides) {
|
|
103
|
-
const
|
|
98
|
+
const availabilityService = createAgentAvailabilityService();
|
|
104
99
|
const profileService = createProfileService();
|
|
100
|
+
const profileSnapshotService = createProfileSnapshotService();
|
|
105
101
|
return {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
statusService: createStatusService({ resolver }),
|
|
109
|
-
doctorService: createDoctorService({ resolver }),
|
|
102
|
+
statusService: createStatusService({ availabilityService }),
|
|
103
|
+
doctorService: createDoctorService({ availabilityService }),
|
|
110
104
|
profileService,
|
|
111
105
|
profileExportService: createProfileExportService({ profileService }),
|
|
112
106
|
profileImportService: createProfileImportService(),
|
|
113
|
-
|
|
107
|
+
profileApplyService: createProfileApplyService({ profileService, snapshotService: profileSnapshotService }),
|
|
108
|
+
profileSnapshotService,
|
|
114
109
|
...overrides
|
|
115
110
|
};
|
|
116
111
|
}
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import type { Command } from 'commander';
|
|
2
|
+
import type { ProfileApplyService } from '../services/profile-apply-service.js';
|
|
2
3
|
import type { ProfileExportService } from '../services/profile-export-service.js';
|
|
3
4
|
import type { ProfileImportService } from '../services/profile-import-service.js';
|
|
4
5
|
import type { ProfileService } from '../services/profile-service.js';
|
|
6
|
+
import type { ProfileSnapshotService } from '../services/profile-snapshot-service.js';
|
|
5
7
|
export interface ProfileCommandServices {
|
|
6
8
|
profileService: ProfileService;
|
|
7
9
|
profileExportService: ProfileExportService;
|
|
8
10
|
profileImportService: ProfileImportService;
|
|
11
|
+
profileApplyService: ProfileApplyService;
|
|
12
|
+
profileSnapshotService: ProfileSnapshotService;
|
|
9
13
|
}
|
|
10
14
|
export declare function registerProfileCommand(program: Command, services: ProfileCommandServices): void;
|
package/dist/commands/profile.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import pc from 'picocolors';
|
|
2
|
+
const ALL_AGENTS = ['claude', 'codex', 'gemini'];
|
|
2
3
|
export function registerProfileCommand(program, services) {
|
|
3
|
-
const { profileService, profileExportService, profileImportService } = services;
|
|
4
|
+
const { profileService, profileExportService, profileImportService, profileApplyService, profileSnapshotService, } = services;
|
|
4
5
|
const profileCmd = program
|
|
5
6
|
.command('profile')
|
|
6
7
|
.description('Manage brainctl profiles');
|
|
@@ -8,15 +9,14 @@ export function registerProfileCommand(program, services) {
|
|
|
8
9
|
.command('list')
|
|
9
10
|
.description('List available profiles')
|
|
10
11
|
.action(async () => {
|
|
11
|
-
const { profiles
|
|
12
|
+
const { profiles } = await profileService.list({ cwd: process.cwd() });
|
|
12
13
|
if (profiles.length === 0) {
|
|
13
14
|
console.log('No profiles found. Run "brainctl profile create <name>" to create one.');
|
|
14
15
|
return;
|
|
15
16
|
}
|
|
16
17
|
console.log(pc.bold('Profiles:'));
|
|
17
18
|
for (const name of profiles) {
|
|
18
|
-
|
|
19
|
-
console.log(` ${name}${marker}`);
|
|
19
|
+
console.log(` ${name}`);
|
|
20
20
|
}
|
|
21
21
|
});
|
|
22
22
|
profileCmd
|
|
@@ -32,21 +32,14 @@ export function registerProfileCommand(program, services) {
|
|
|
32
32
|
});
|
|
33
33
|
console.log(`Created profile at ${result.profilePath}`);
|
|
34
34
|
});
|
|
35
|
-
profileCmd
|
|
36
|
-
.command('use')
|
|
37
|
-
.argument('<name>', 'Profile name to activate')
|
|
38
|
-
.description('Switch the active profile')
|
|
39
|
-
.action(async (name) => {
|
|
40
|
-
const result = await profileService.use({ cwd: process.cwd(), name });
|
|
41
|
-
const prev = result.previousProfile ? ` (was "${result.previousProfile}")` : '';
|
|
42
|
-
console.log(`Switched to profile "${name}"${prev}`);
|
|
43
|
-
});
|
|
44
35
|
profileCmd
|
|
45
36
|
.command('export')
|
|
46
37
|
.argument('[name]', 'Profile name to export')
|
|
47
38
|
.option('-a, --agent <name>', 'Pack a live agent config instead (claude, codex, gemini)')
|
|
48
|
-
.option('-o, --output <path>', 'Output file path')
|
|
49
|
-
.
|
|
39
|
+
.option('-o, --output <path>', 'Output file or directory path')
|
|
40
|
+
.option('-f, --format <format>', 'Output format: tarball (default) or folder', 'tarball')
|
|
41
|
+
.option('--credentials <mode>', 'How to handle secrets: redact (default, public-safe) or keep (writes .env with real values for self-sync)', 'redact')
|
|
42
|
+
.description('Export a profile as a portable tarball or folder')
|
|
50
43
|
.action(async (name, options) => {
|
|
51
44
|
const agent = options.agent === 'claude' || options.agent === 'codex' || options.agent === 'gemini'
|
|
52
45
|
? options.agent
|
|
@@ -54,14 +47,28 @@ export function registerProfileCommand(program, services) {
|
|
|
54
47
|
if (!agent && !name) {
|
|
55
48
|
throw new Error('Provide a profile name or --agent <name>.');
|
|
56
49
|
}
|
|
50
|
+
if (options.format && options.format !== 'tarball' && options.format !== 'folder') {
|
|
51
|
+
throw new Error(`Invalid --format "${options.format}". Use "tarball" or "folder".`);
|
|
52
|
+
}
|
|
53
|
+
if (options.credentials &&
|
|
54
|
+
options.credentials !== 'redact' &&
|
|
55
|
+
options.credentials !== 'keep') {
|
|
56
|
+
throw new Error(`Invalid --credentials "${options.credentials}". Use "redact" or "keep".`);
|
|
57
|
+
}
|
|
57
58
|
const result = await profileExportService.execute({
|
|
58
59
|
cwd: process.cwd(),
|
|
59
60
|
source: agent
|
|
60
61
|
? { source: 'agent', agent, cwd: process.cwd() }
|
|
61
62
|
: { source: 'profile', name: name },
|
|
62
63
|
outputPath: options.output,
|
|
64
|
+
format: options.format ?? 'tarball',
|
|
65
|
+
credentialsMode: options.credentials ?? 'redact',
|
|
63
66
|
});
|
|
64
|
-
|
|
67
|
+
for (const warning of result.warnings) {
|
|
68
|
+
console.warn(pc.yellow(`warning: ${warning}`));
|
|
69
|
+
}
|
|
70
|
+
const label = result.format === 'folder' ? 'profile folder' : 'profile tarball';
|
|
71
|
+
console.log(`Exported ${label} to ${result.archivePath}`);
|
|
65
72
|
});
|
|
66
73
|
profileCmd
|
|
67
74
|
.command('import')
|
|
@@ -81,6 +88,89 @@ export function registerProfileCommand(program, services) {
|
|
|
81
88
|
console.log(`Installed bundled MCPs: ${result.installedMcps.join(', ')}`);
|
|
82
89
|
}
|
|
83
90
|
});
|
|
91
|
+
profileCmd
|
|
92
|
+
.command('apply')
|
|
93
|
+
.argument('<name>', 'Profile name to apply')
|
|
94
|
+
.option('-a, --agent <list>', 'Comma-separated agents to target (claude, codex, gemini, or all)', 'all')
|
|
95
|
+
.option('-i, --items <list>', 'Comma-separated items to apply (e.g. mcp:github,plugin:demo,skill:reviewer). Default: everything matching.')
|
|
96
|
+
.option('--no-backup', 'Skip auto-backup of live agent state before applying')
|
|
97
|
+
.description('Apply a profile (MCPs + plugins + skills) to selected agents')
|
|
98
|
+
.action(async (name, options) => {
|
|
99
|
+
const agents = parseAgentList(options.agent);
|
|
100
|
+
const items = options.items ? parseItemList(options.items) : undefined;
|
|
101
|
+
const { backups, applied } = await profileApplyService.execute({
|
|
102
|
+
cwd: process.cwd(),
|
|
103
|
+
profileName: name,
|
|
104
|
+
agents,
|
|
105
|
+
items,
|
|
106
|
+
backup: options.backup,
|
|
107
|
+
});
|
|
108
|
+
if (backups.length > 0) {
|
|
109
|
+
console.log(pc.bold('Backups:'));
|
|
110
|
+
for (const b of backups) {
|
|
111
|
+
console.log(` ${b.agent} -> ${b.profileName}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
console.log(pc.bold(`Applied "${name}" to:`));
|
|
115
|
+
for (const r of applied) {
|
|
116
|
+
const extras = [`${r.mcpCount} MCPs`];
|
|
117
|
+
if (r.pluginsInstalled?.length)
|
|
118
|
+
extras.push(`plugins: ${r.pluginsInstalled.join(',')}`);
|
|
119
|
+
if (r.userSkillsInstalled?.length)
|
|
120
|
+
extras.push(`skills: ${r.userSkillsInstalled.join(',')}`);
|
|
121
|
+
console.log(` ${r.agent}: ${extras.join(' | ')}`);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
profileCmd
|
|
125
|
+
.command('snapshot')
|
|
126
|
+
.option('-a, --agent <name>', 'Agent to snapshot (claude, codex, gemini)')
|
|
127
|
+
.option('--as <name>', 'Profile name to write into (default: backup-<agent>-<timestamp>)')
|
|
128
|
+
.description("Snapshot a live agent's MCPs+plugins+skills into a new profile folder")
|
|
129
|
+
.action(async (options) => {
|
|
130
|
+
if (!options.agent || !ALL_AGENTS.includes(options.agent)) {
|
|
131
|
+
throw new Error('Provide --agent <claude|codex|gemini>.');
|
|
132
|
+
}
|
|
133
|
+
const agent = options.agent;
|
|
134
|
+
const { defaultBackupProfileName } = await import('../services/profile-snapshot-service.js');
|
|
135
|
+
const profileName = options.as ?? defaultBackupProfileName(agent);
|
|
136
|
+
const result = await profileSnapshotService.execute({
|
|
137
|
+
cwd: process.cwd(),
|
|
138
|
+
agent,
|
|
139
|
+
profileName,
|
|
140
|
+
});
|
|
141
|
+
console.log(`Snapshotted ${agent} into ${result.profilePath}`);
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
function parseAgentList(value) {
|
|
145
|
+
if (value === 'all')
|
|
146
|
+
return [...ALL_AGENTS];
|
|
147
|
+
const parts = value.split(',').map((s) => s.trim()).filter(Boolean);
|
|
148
|
+
for (const p of parts) {
|
|
149
|
+
if (!ALL_AGENTS.includes(p)) {
|
|
150
|
+
throw new Error(`Invalid agent "${p}". Use claude, codex, gemini, or all.`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return parts;
|
|
154
|
+
}
|
|
155
|
+
function parseItemList(value) {
|
|
156
|
+
return value
|
|
157
|
+
.split(',')
|
|
158
|
+
.map((s) => s.trim())
|
|
159
|
+
.filter(Boolean)
|
|
160
|
+
.map((entry) => {
|
|
161
|
+
const colonIdx = entry.indexOf(':');
|
|
162
|
+
if (colonIdx <= 0) {
|
|
163
|
+
throw new Error(`Invalid item "${entry}". Use type:name (e.g. mcp:github).`);
|
|
164
|
+
}
|
|
165
|
+
const type = entry.slice(0, colonIdx);
|
|
166
|
+
const name = entry.slice(colonIdx + 1);
|
|
167
|
+
if (type !== 'mcp' && type !== 'plugin' && type !== 'skill') {
|
|
168
|
+
throw new Error(`Invalid item type "${type}". Use mcp, plugin, or skill.`);
|
|
169
|
+
}
|
|
170
|
+
if (!name)
|
|
171
|
+
throw new Error(`Item "${entry}" missing name.`);
|
|
172
|
+
return { type, name };
|
|
173
|
+
});
|
|
84
174
|
}
|
|
85
175
|
function collectCredentialOption(value, previous) {
|
|
86
176
|
return [...previous, value];
|
package/dist/commands/status.js
CHANGED
|
@@ -2,17 +2,17 @@ import pc from 'picocolors';
|
|
|
2
2
|
export function registerStatusCommand(program, statusService) {
|
|
3
3
|
program
|
|
4
4
|
.command('status')
|
|
5
|
-
.description('Show
|
|
5
|
+
.description('Show agent availability and profile inventory')
|
|
6
6
|
.action(async () => {
|
|
7
7
|
const status = await statusService.execute({ cwd: process.cwd() });
|
|
8
8
|
console.log(pc.bold('brainctl status'));
|
|
9
|
-
console.log(`
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
console.log('
|
|
9
|
+
console.log(`Profiles: ${status.profiles.count}`);
|
|
10
|
+
for (const name of status.profiles.names) {
|
|
11
|
+
console.log(` ${name}`);
|
|
12
|
+
}
|
|
13
|
+
console.log('Agents:');
|
|
14
14
|
for (const agent of Object.values(status.agents)) {
|
|
15
|
-
console.log(
|
|
15
|
+
console.log(` ${agent.agent}: ${agent.available ? pc.green('available') : pc.yellow('missing')}`);
|
|
16
16
|
}
|
|
17
17
|
});
|
|
18
18
|
}
|
package/dist/mcp/server.d.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { FastMCP } from 'fastmcp';
|
|
2
|
+
import { type UiServer } from '../ui/server.js';
|
|
3
|
+
export interface UiServerState {
|
|
4
|
+
current: UiServer | null;
|
|
5
|
+
}
|
|
2
6
|
export declare function createMcpServer(options?: {
|
|
3
7
|
cwd?: string;
|
|
8
|
+
uiServerState?: UiServerState;
|
|
4
9
|
}): FastMCP;
|
|
5
10
|
export declare function startMcpServer(options?: {
|
|
6
11
|
cwd?: string;
|