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
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import { lstat, mkdir, realpath, writeFile } from 'node:fs/promises';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import YAML from 'yaml';
|
|
4
|
-
import { ConfigError } from '../errors.js';
|
|
5
|
-
export function createConfigWriteService() {
|
|
6
|
-
return {
|
|
7
|
-
async execute(request) {
|
|
8
|
-
const cwd = request.cwd ?? process.cwd();
|
|
9
|
-
const configPath = path.join(cwd, 'ai-stack.yaml');
|
|
10
|
-
const memoryPaths = await Promise.all(request.config.memory.paths.map((memoryPath) => normalizeMemoryPath(cwd, memoryPath)));
|
|
11
|
-
const payload = {
|
|
12
|
-
memory: {
|
|
13
|
-
paths: memoryPaths
|
|
14
|
-
},
|
|
15
|
-
skills: request.config.skills,
|
|
16
|
-
mcps: request.config.mcps
|
|
17
|
-
};
|
|
18
|
-
await mkdir(path.dirname(configPath), { recursive: true });
|
|
19
|
-
await writeFile(configPath, `${YAML.stringify(payload)}`, 'utf8');
|
|
20
|
-
return { configPath };
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
async function normalizeMemoryPath(cwd, filePath) {
|
|
25
|
-
const workspaceRoot = await realpath(cwd);
|
|
26
|
-
const resolvedPath = path.resolve(cwd, filePath);
|
|
27
|
-
const realTargetPath = await resolvePathForWrite(resolvedPath);
|
|
28
|
-
if (!isWithinDirectory(workspaceRoot, realTargetPath)) {
|
|
29
|
-
throw new ConfigError('Memory paths must stay within the workspace root.');
|
|
30
|
-
}
|
|
31
|
-
const relativePath = path.relative(cwd, resolvedPath);
|
|
32
|
-
return relativePath.length > 0 ? relativePath : '.';
|
|
33
|
-
}
|
|
34
|
-
async function resolvePathForWrite(targetPath) {
|
|
35
|
-
const existingPath = await findNearestExistingPath(targetPath);
|
|
36
|
-
const resolvedExistingPath = await realpath(existingPath);
|
|
37
|
-
if (existingPath === targetPath) {
|
|
38
|
-
return resolvedExistingPath;
|
|
39
|
-
}
|
|
40
|
-
return path.resolve(resolvedExistingPath, path.relative(existingPath, targetPath));
|
|
41
|
-
}
|
|
42
|
-
async function findNearestExistingPath(targetPath) {
|
|
43
|
-
let currentPath = targetPath;
|
|
44
|
-
while (true) {
|
|
45
|
-
try {
|
|
46
|
-
await lstat(currentPath);
|
|
47
|
-
return currentPath;
|
|
48
|
-
}
|
|
49
|
-
catch (error) {
|
|
50
|
-
if (!isMissingPathError(error)) {
|
|
51
|
-
throw error;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
const parentPath = path.dirname(currentPath);
|
|
55
|
-
if (parentPath === currentPath) {
|
|
56
|
-
throw new ConfigError(`Could not resolve filesystem path for ${targetPath}.`);
|
|
57
|
-
}
|
|
58
|
-
currentPath = parentPath;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
function isWithinDirectory(parentDirectory, targetPath) {
|
|
62
|
-
const relativePath = path.relative(parentDirectory, targetPath);
|
|
63
|
-
if (relativePath === '') {
|
|
64
|
-
return true;
|
|
65
|
-
}
|
|
66
|
-
return !relativePath.startsWith(`..${path.sep}`) && relativePath !== '..' && !path.isAbsolute(relativePath);
|
|
67
|
-
}
|
|
68
|
-
function isMissingPathError(error) {
|
|
69
|
-
return error instanceof Error && 'code' in error && error.code === 'ENOENT';
|
|
70
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
export interface InitServiceRequest {
|
|
2
|
-
cwd?: string;
|
|
3
|
-
force?: boolean;
|
|
4
|
-
}
|
|
5
|
-
export interface InitServiceResult {
|
|
6
|
-
created: string[];
|
|
7
|
-
replaced: string[];
|
|
8
|
-
skipped: string[];
|
|
9
|
-
alreadyInitialized: boolean;
|
|
10
|
-
}
|
|
11
|
-
export interface InitService {
|
|
12
|
-
execute(request?: InitServiceRequest): Promise<InitServiceResult>;
|
|
13
|
-
}
|
|
14
|
-
export declare function createInitService(): InitService;
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { mkdir, stat, writeFile } from 'node:fs/promises';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
const SAMPLE_CONFIG = `memory:
|
|
4
|
-
paths:
|
|
5
|
-
- ./memory
|
|
6
|
-
|
|
7
|
-
skills:
|
|
8
|
-
summarize:
|
|
9
|
-
description: Summarize content
|
|
10
|
-
prompt: |
|
|
11
|
-
Summarize the following content into concise bullet points.
|
|
12
|
-
|
|
13
|
-
analyze:
|
|
14
|
-
description: Analyze content deeply
|
|
15
|
-
prompt: |
|
|
16
|
-
Analyze the following content and extract key insights.
|
|
17
|
-
|
|
18
|
-
mcps: {}
|
|
19
|
-
`;
|
|
20
|
-
const SAMPLE_MEMORY = `# Team Notes
|
|
21
|
-
|
|
22
|
-
- Track important project context here.
|
|
23
|
-
- Keep prompts and references concise.
|
|
24
|
-
`;
|
|
25
|
-
export function createInitService() {
|
|
26
|
-
return {
|
|
27
|
-
async execute(request = {}) {
|
|
28
|
-
const cwd = request.cwd ?? process.cwd();
|
|
29
|
-
const force = request.force ?? false;
|
|
30
|
-
const configPath = path.join(cwd, 'ai-stack.yaml');
|
|
31
|
-
const memoryDir = path.join(cwd, 'memory');
|
|
32
|
-
const notesPath = path.join(memoryDir, 'notes.md');
|
|
33
|
-
const created = [];
|
|
34
|
-
const replaced = [];
|
|
35
|
-
const skipped = [];
|
|
36
|
-
await writeManagedFile({
|
|
37
|
-
targetPath: configPath,
|
|
38
|
-
content: SAMPLE_CONFIG,
|
|
39
|
-
force,
|
|
40
|
-
created,
|
|
41
|
-
replaced,
|
|
42
|
-
skipped
|
|
43
|
-
});
|
|
44
|
-
const memoryDirExists = await pathExists(memoryDir);
|
|
45
|
-
if (!memoryDirExists) {
|
|
46
|
-
await mkdir(memoryDir, { recursive: true });
|
|
47
|
-
created.push(memoryDir);
|
|
48
|
-
}
|
|
49
|
-
await writeManagedFile({
|
|
50
|
-
targetPath: notesPath,
|
|
51
|
-
content: SAMPLE_MEMORY,
|
|
52
|
-
force,
|
|
53
|
-
created,
|
|
54
|
-
replaced,
|
|
55
|
-
skipped
|
|
56
|
-
});
|
|
57
|
-
return {
|
|
58
|
-
created,
|
|
59
|
-
replaced,
|
|
60
|
-
skipped,
|
|
61
|
-
alreadyInitialized: created.length === 0 && replaced.length === 0
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
async function writeManagedFile(options) {
|
|
67
|
-
const exists = await pathExists(options.targetPath);
|
|
68
|
-
if (exists && !options.force) {
|
|
69
|
-
options.skipped.push(options.targetPath);
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
await mkdir(path.dirname(options.targetPath), { recursive: true });
|
|
73
|
-
await writeFile(options.targetPath, options.content, 'utf8');
|
|
74
|
-
if (exists) {
|
|
75
|
-
options.replaced.push(options.targetPath);
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
options.created.push(options.targetPath);
|
|
79
|
-
}
|
|
80
|
-
async function pathExists(targetPath) {
|
|
81
|
-
try {
|
|
82
|
-
await stat(targetPath);
|
|
83
|
-
return true;
|
|
84
|
-
}
|
|
85
|
-
catch {
|
|
86
|
-
return false;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export interface MemoryWriteRequest {
|
|
2
|
-
cwd?: string;
|
|
3
|
-
filePath: string;
|
|
4
|
-
content: string;
|
|
5
|
-
}
|
|
6
|
-
export interface MemoryWriteResult {
|
|
7
|
-
filePath: string;
|
|
8
|
-
}
|
|
9
|
-
export interface MemoryWriteService {
|
|
10
|
-
execute(request: MemoryWriteRequest): Promise<MemoryWriteResult>;
|
|
11
|
-
}
|
|
12
|
-
export declare function createMemoryWriteService(): MemoryWriteService;
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { lstat, mkdir, realpath, writeFile } from 'node:fs/promises';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { MemoryPathError } from '../errors.js';
|
|
4
|
-
export function createMemoryWriteService() {
|
|
5
|
-
return {
|
|
6
|
-
async execute(request) {
|
|
7
|
-
const cwd = request.cwd ?? process.cwd();
|
|
8
|
-
const targetPath = path.resolve(cwd, request.filePath);
|
|
9
|
-
const workspaceRoot = await realpath(cwd);
|
|
10
|
-
const resolvedTargetPath = await resolvePathForWrite(targetPath);
|
|
11
|
-
if (!isWithinDirectory(workspaceRoot, resolvedTargetPath)) {
|
|
12
|
-
throw new MemoryPathError('Memory files must stay within the workspace root.');
|
|
13
|
-
}
|
|
14
|
-
await mkdir(path.dirname(targetPath), { recursive: true });
|
|
15
|
-
await writeFile(targetPath, request.content, 'utf8');
|
|
16
|
-
return { filePath: targetPath };
|
|
17
|
-
}
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
async function resolvePathForWrite(targetPath) {
|
|
21
|
-
const existingPath = await findNearestExistingPath(targetPath);
|
|
22
|
-
const resolvedExistingPath = await realpath(existingPath);
|
|
23
|
-
if (existingPath === targetPath) {
|
|
24
|
-
return resolvedExistingPath;
|
|
25
|
-
}
|
|
26
|
-
return path.resolve(resolvedExistingPath, path.relative(existingPath, targetPath));
|
|
27
|
-
}
|
|
28
|
-
async function findNearestExistingPath(targetPath) {
|
|
29
|
-
let currentPath = targetPath;
|
|
30
|
-
while (true) {
|
|
31
|
-
try {
|
|
32
|
-
await lstat(currentPath);
|
|
33
|
-
return currentPath;
|
|
34
|
-
}
|
|
35
|
-
catch (error) {
|
|
36
|
-
if (!isMissingPathError(error)) {
|
|
37
|
-
throw error;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
const parentPath = path.dirname(currentPath);
|
|
41
|
-
if (parentPath === currentPath) {
|
|
42
|
-
throw new MemoryPathError(`Could not resolve filesystem path for ${targetPath}.`);
|
|
43
|
-
}
|
|
44
|
-
currentPath = parentPath;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
function isWithinDirectory(parentDirectory, targetPath) {
|
|
48
|
-
const relativePath = path.relative(parentDirectory, targetPath);
|
|
49
|
-
if (relativePath === '') {
|
|
50
|
-
return true;
|
|
51
|
-
}
|
|
52
|
-
return !relativePath.startsWith(`..${path.sep}`) && relativePath !== '..' && !path.isAbsolute(relativePath);
|
|
53
|
-
}
|
|
54
|
-
function isMissingPathError(error) {
|
|
55
|
-
return error instanceof Error && 'code' in error && error.code === 'ENOENT';
|
|
56
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type { ExecutorResolver } from '../executor/resolver.js';
|
|
2
|
-
import type { ExecutionStep, ExecutionTrace, RunRequest } from '../types.js';
|
|
3
|
-
export interface RunService {
|
|
4
|
-
execute(request: RunRequest, options?: RunServiceExecuteOptions): Promise<ExecutionTrace>;
|
|
5
|
-
}
|
|
6
|
-
export interface RunServiceExecuteOptions {
|
|
7
|
-
onOutputChunk?: (chunk: string) => void;
|
|
8
|
-
streamOutput?: boolean;
|
|
9
|
-
}
|
|
10
|
-
interface RunServiceDependencies {
|
|
11
|
-
resolver?: ExecutorResolver;
|
|
12
|
-
}
|
|
13
|
-
export declare function createRunService(dependencies?: RunServiceDependencies): RunService;
|
|
14
|
-
export declare function buildExecutionPlan(request: RunRequest): ExecutionStep[];
|
|
15
|
-
export {};
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import { readFile } from 'node:fs/promises';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { AgentNotAvailableError, InputFileError } from '../errors.js';
|
|
4
|
-
import { loadConfig } from '../config.js';
|
|
5
|
-
import { buildContext } from '../context/builder.js';
|
|
6
|
-
import { loadMemory } from '../context/memory.js';
|
|
7
|
-
import { resolveSkillPrompt } from '../context/skills.js';
|
|
8
|
-
import { createExecutorResolver } from '../executor/resolver.js';
|
|
9
|
-
export function createRunService(dependencies = {}) {
|
|
10
|
-
const resolver = dependencies.resolver ?? createExecutorResolver();
|
|
11
|
-
return {
|
|
12
|
-
async execute(request, options = {}) {
|
|
13
|
-
const cwd = request.cwd ?? process.cwd();
|
|
14
|
-
const config = await loadConfig({ cwd });
|
|
15
|
-
const memory = await loadMemory({ paths: config.memory.paths });
|
|
16
|
-
const steps = buildExecutionPlan(request);
|
|
17
|
-
const results = [];
|
|
18
|
-
let previousOutput;
|
|
19
|
-
for (const [stepIndex, step] of steps.entries()) {
|
|
20
|
-
const input = await resolveInput(step, cwd, previousOutput);
|
|
21
|
-
const skill = resolveSkillPrompt(config, step.skill);
|
|
22
|
-
const context = buildContext({
|
|
23
|
-
memory: memory.content,
|
|
24
|
-
skill,
|
|
25
|
-
input
|
|
26
|
-
});
|
|
27
|
-
let executor = await resolvePrimaryExecutor(resolver, step);
|
|
28
|
-
let fallbackUsed = false;
|
|
29
|
-
if (executor.fallbackRequired) {
|
|
30
|
-
fallbackUsed = true;
|
|
31
|
-
}
|
|
32
|
-
const result = await executor.instance.run(context, {
|
|
33
|
-
streamOutput: options.streamOutput ?? true,
|
|
34
|
-
onOutputChunk: options.onOutputChunk
|
|
35
|
-
});
|
|
36
|
-
previousOutput = result.output;
|
|
37
|
-
results.push({
|
|
38
|
-
stepIndex,
|
|
39
|
-
requestedAgent: step.primaryAgent,
|
|
40
|
-
agent: result.agent,
|
|
41
|
-
fallbackUsed,
|
|
42
|
-
exitCode: result.exitCode,
|
|
43
|
-
output: result.output
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
const finalResult = results.at(-1);
|
|
47
|
-
return {
|
|
48
|
-
steps: results,
|
|
49
|
-
finalOutput: finalResult?.output ?? '',
|
|
50
|
-
finalExitCode: finalResult?.exitCode ?? 0
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
export function buildExecutionPlan(request) {
|
|
56
|
-
return [
|
|
57
|
-
{
|
|
58
|
-
skill: request.skill,
|
|
59
|
-
inputFile: request.inputFile,
|
|
60
|
-
primaryAgent: request.primaryAgent,
|
|
61
|
-
fallbackAgent: request.fallbackAgent,
|
|
62
|
-
usePreviousOutput: false
|
|
63
|
-
}
|
|
64
|
-
];
|
|
65
|
-
}
|
|
66
|
-
async function resolveInput(step, cwd, previousOutput) {
|
|
67
|
-
if (step.usePreviousOutput && previousOutput !== undefined) {
|
|
68
|
-
return previousOutput;
|
|
69
|
-
}
|
|
70
|
-
const inputPath = path.resolve(cwd, step.inputFile);
|
|
71
|
-
try {
|
|
72
|
-
return await readFile(inputPath, 'utf8');
|
|
73
|
-
}
|
|
74
|
-
catch (error) {
|
|
75
|
-
throw new InputFileError(`Could not read input file: ${step.inputFile}`);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
async function resolvePrimaryExecutor(resolver, step) {
|
|
79
|
-
try {
|
|
80
|
-
return {
|
|
81
|
-
instance: await resolver.resolveExecutor(step.primaryAgent),
|
|
82
|
-
fallbackRequired: false
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
catch (error) {
|
|
86
|
-
if (!(error instanceof AgentNotAvailableError) || !step.fallbackAgent) {
|
|
87
|
-
throw error;
|
|
88
|
-
}
|
|
89
|
-
return {
|
|
90
|
-
instance: await resolver.resolveExecutor(step.fallbackAgent),
|
|
91
|
-
fallbackRequired: true
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type { AgentName, SyncResult } from '../types.js';
|
|
2
|
-
import type { AgentConfigWriter } from './sync/agent-writer.js';
|
|
3
|
-
import { type ProfileService } from './profile-service.js';
|
|
4
|
-
export interface SyncService {
|
|
5
|
-
execute(options?: {
|
|
6
|
-
cwd?: string;
|
|
7
|
-
restore?: boolean;
|
|
8
|
-
}): Promise<SyncResult>;
|
|
9
|
-
}
|
|
10
|
-
interface SyncServiceDependencies {
|
|
11
|
-
profileService?: ProfileService;
|
|
12
|
-
writers?: Partial<Record<AgentName, AgentConfigWriter>>;
|
|
13
|
-
}
|
|
14
|
-
export declare function createSyncService(dependencies?: SyncServiceDependencies): SyncService;
|
|
15
|
-
export {};
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { ProfileError } from '../errors.js';
|
|
2
|
-
import { createClaudeWriter } from './sync/claude-writer.js';
|
|
3
|
-
import { createCodexWriter } from './sync/codex-writer.js';
|
|
4
|
-
import { createGeminiWriter } from './sync/gemini-writer.js';
|
|
5
|
-
import { createProfileService } from './profile-service.js';
|
|
6
|
-
export function createSyncService(dependencies = {}) {
|
|
7
|
-
const profileService = dependencies.profileService ?? createProfileService();
|
|
8
|
-
const defaultWriters = {
|
|
9
|
-
claude: createClaudeWriter(),
|
|
10
|
-
codex: createCodexWriter(),
|
|
11
|
-
gemini: createGeminiWriter(),
|
|
12
|
-
};
|
|
13
|
-
const writers = { ...defaultWriters, ...dependencies.writers };
|
|
14
|
-
return {
|
|
15
|
-
async execute(options = {}) {
|
|
16
|
-
const cwd = options.cwd ?? process.cwd();
|
|
17
|
-
if (options.restore) {
|
|
18
|
-
return restoreAll(writers, cwd);
|
|
19
|
-
}
|
|
20
|
-
const meta = await profileService.getMetaConfig({ cwd });
|
|
21
|
-
if (!meta.active_profile) {
|
|
22
|
-
throw new Error('No active profile set. Run "brainctl profile use <name>" first.');
|
|
23
|
-
}
|
|
24
|
-
const profile = await profileService.get({ cwd, name: meta.active_profile });
|
|
25
|
-
const remoteMcpName = Object.entries(profile.mcps).find(([, config]) => config.kind === 'remote')?.[0];
|
|
26
|
-
if (remoteMcpName) {
|
|
27
|
-
throw new ProfileError(`Profile "${profile.name}" includes remote MCP "${remoteMcpName}". Remote MCP sync is not supported yet.`);
|
|
28
|
-
}
|
|
29
|
-
const results = [];
|
|
30
|
-
for (const agent of meta.agents) {
|
|
31
|
-
const writer = writers[agent];
|
|
32
|
-
if (!writer) {
|
|
33
|
-
continue;
|
|
34
|
-
}
|
|
35
|
-
const result = await writer.write({
|
|
36
|
-
mcpServers: profile.mcps,
|
|
37
|
-
cwd,
|
|
38
|
-
});
|
|
39
|
-
results.push({
|
|
40
|
-
agent,
|
|
41
|
-
configPath: result.configPath,
|
|
42
|
-
backedUpTo: result.backedUpTo,
|
|
43
|
-
mcpCount: Object.keys(profile.mcps).length,
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
return results;
|
|
47
|
-
},
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
async function restoreAll(writers, cwd) {
|
|
51
|
-
const results = [];
|
|
52
|
-
for (const [agent, writer] of Object.entries(writers)) {
|
|
53
|
-
if (!writer)
|
|
54
|
-
continue;
|
|
55
|
-
try {
|
|
56
|
-
const { restoredFrom } = await writer.restore({ cwd });
|
|
57
|
-
results.push({
|
|
58
|
-
agent: agent,
|
|
59
|
-
configPath: restoredFrom,
|
|
60
|
-
backedUpTo: null,
|
|
61
|
-
mcpCount: 0,
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
catch {
|
|
65
|
-
// Skip agents with no backup
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
return results;
|
|
69
|
-
}
|
package/dist/ui/streaming.d.ts
DELETED
package/dist/ui/streaming.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
export function startSseStream(response) {
|
|
2
|
-
response.statusCode = 200;
|
|
3
|
-
response.setHeader('Content-Type', 'text/event-stream; charset=utf-8');
|
|
4
|
-
response.setHeader('Cache-Control', 'no-cache, no-transform');
|
|
5
|
-
response.setHeader('Connection', 'keep-alive');
|
|
6
|
-
response.setHeader('X-Accel-Buffering', 'no');
|
|
7
|
-
response.flushHeaders?.();
|
|
8
|
-
}
|
|
9
|
-
export function writeSseEvent(response, event, data) {
|
|
10
|
-
response.write(`event: ${event}\n`);
|
|
11
|
-
const payload = typeof data === 'string' ? data : JSON.stringify(data);
|
|
12
|
-
for (const line of payload.split(/\r?\n/)) {
|
|
13
|
-
response.write(`data: ${line}\n`);
|
|
14
|
-
}
|
|
15
|
-
response.write('\n');
|
|
16
|
-
}
|