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,79 +1,23 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { loadConfig } from '../config.js';
|
|
4
|
-
import { createExecutorResolver } from '../executor/resolver.js';
|
|
1
|
+
import { createAgentAvailabilityService, } from './agent-availability-service.js';
|
|
5
2
|
export function createDoctorService(dependencies = {}) {
|
|
6
|
-
const
|
|
3
|
+
const availabilityService = dependencies.availabilityService ?? createAgentAvailabilityService();
|
|
7
4
|
return {
|
|
8
|
-
async execute(
|
|
9
|
-
const cwd = options.cwd ?? process.cwd();
|
|
5
|
+
async execute() {
|
|
10
6
|
const checks = [];
|
|
11
|
-
const
|
|
12
|
-
const configExists = await pathExists(configPath);
|
|
13
|
-
if (!configExists) {
|
|
14
|
-
checks.push({
|
|
15
|
-
label: 'Config',
|
|
16
|
-
status: 'error',
|
|
17
|
-
message: 'ai-stack.yaml was not found.'
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
if (configExists) {
|
|
21
|
-
try {
|
|
22
|
-
const config = await loadConfig({ cwd });
|
|
23
|
-
checks.push({
|
|
24
|
-
label: 'Config',
|
|
25
|
-
status: 'ok',
|
|
26
|
-
message: `Loaded ${config.configPath}`
|
|
27
|
-
});
|
|
28
|
-
for (const memoryPath of config.memory.paths) {
|
|
29
|
-
const exists = await pathExists(memoryPath);
|
|
30
|
-
checks.push({
|
|
31
|
-
label: 'Memory',
|
|
32
|
-
status: exists ? 'ok' : 'error',
|
|
33
|
-
message: exists
|
|
34
|
-
? `Memory path is available: ${memoryPath}`
|
|
35
|
-
: `Memory path is missing: ${memoryPath}`
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
checks.push({
|
|
39
|
-
label: 'Skills',
|
|
40
|
-
status: Object.keys(config.skills).length > 0 ? 'ok' : 'error',
|
|
41
|
-
message: Object.keys(config.skills).length > 0
|
|
42
|
-
? `${Object.keys(config.skills).length} skills configured`
|
|
43
|
-
: 'No skills are configured.'
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
catch (error) {
|
|
47
|
-
checks.push({
|
|
48
|
-
label: 'Config',
|
|
49
|
-
status: 'error',
|
|
50
|
-
message: error instanceof Error ? error.message : 'Config validation failed.'
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
const availability = await resolver.getAgentAvailability();
|
|
7
|
+
const availability = await availabilityService.getAll();
|
|
55
8
|
for (const agent of Object.values(availability)) {
|
|
56
9
|
checks.push({
|
|
57
10
|
label: 'Agent',
|
|
58
11
|
status: agent.available ? 'ok' : 'warn',
|
|
59
12
|
message: agent.available
|
|
60
13
|
? `${agent.agent} is available`
|
|
61
|
-
: `${agent.agent} is not available on PATH
|
|
14
|
+
: `${agent.agent} is not available on PATH`,
|
|
62
15
|
});
|
|
63
16
|
}
|
|
64
17
|
return {
|
|
65
18
|
checks,
|
|
66
|
-
hasIssues: checks.some((
|
|
19
|
+
hasIssues: checks.some((c) => c.status !== 'ok'),
|
|
67
20
|
};
|
|
68
|
-
}
|
|
21
|
+
},
|
|
69
22
|
};
|
|
70
23
|
}
|
|
71
|
-
async function pathExists(targetPath) {
|
|
72
|
-
try {
|
|
73
|
-
await stat(targetPath);
|
|
74
|
-
return true;
|
|
75
|
-
}
|
|
76
|
-
catch {
|
|
77
|
-
return false;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
@@ -9,13 +9,19 @@ export type PortablePackSource = {
|
|
|
9
9
|
agent: AgentName;
|
|
10
10
|
cwd: string;
|
|
11
11
|
};
|
|
12
|
+
export type PortablePackFormat = 'tarball' | 'folder';
|
|
13
|
+
export type PortableCredentialsMode = 'redact' | 'keep';
|
|
12
14
|
export interface PortableProfilePackService {
|
|
13
15
|
execute(options: {
|
|
14
16
|
cwd?: string;
|
|
15
17
|
source: PortablePackSource;
|
|
16
18
|
outputPath?: string;
|
|
19
|
+
format?: PortablePackFormat;
|
|
20
|
+
credentialsMode?: PortableCredentialsMode;
|
|
17
21
|
}): Promise<{
|
|
18
22
|
archivePath: string;
|
|
23
|
+
format: PortablePackFormat;
|
|
24
|
+
warnings: string[];
|
|
19
25
|
}>;
|
|
20
26
|
}
|
|
21
27
|
interface PortableProfilePackServiceDependencies {
|
|
@@ -52,11 +52,26 @@ export function createPortableProfilePackService(deps = {}) {
|
|
|
52
52
|
filter: (src) => !matchesPluginExclude(src),
|
|
53
53
|
});
|
|
54
54
|
}
|
|
55
|
+
const format = options.format ?? 'tarball';
|
|
56
|
+
const credentialsMode = options.credentialsMode ?? 'redact';
|
|
57
|
+
const warnings = [];
|
|
58
|
+
if (credentialsMode === 'keep' && Object.keys(packed.rawCredentials).length > 0) {
|
|
59
|
+
await writeFile(path.join(stagingDir, '.env'), renderDotEnv(packed.rawCredentials), 'utf8');
|
|
60
|
+
warnings.push(`Wrote .env with ${Object.keys(packed.rawCredentials).length} real credential value(s). Do NOT publish this archive publicly.`);
|
|
61
|
+
}
|
|
62
|
+
if (format === 'folder') {
|
|
63
|
+
await writeRepoReadyFiles(stagingDir, packed);
|
|
64
|
+
const outputPath = options.outputPath ?? path.join(cwd, packed.profile.name);
|
|
65
|
+
await rm(outputPath, { recursive: true, force: true });
|
|
66
|
+
await mkdir(path.dirname(outputPath), { recursive: true });
|
|
67
|
+
await cp(stagingDir, outputPath, { recursive: true });
|
|
68
|
+
return { archivePath: outputPath, format, warnings };
|
|
69
|
+
}
|
|
55
70
|
const outputPath = options.outputPath ?? path.join(cwd, `${packed.profile.name}.tar.gz`);
|
|
56
71
|
execSync(`tar -czf "${outputPath}" -C "${stagingDir}" .`, {
|
|
57
72
|
stdio: 'pipe',
|
|
58
73
|
});
|
|
59
|
-
return { archivePath: outputPath };
|
|
74
|
+
return { archivePath: outputPath, format, warnings };
|
|
60
75
|
}
|
|
61
76
|
finally {
|
|
62
77
|
await rm(stagingDir, { recursive: true, force: true });
|
|
@@ -64,6 +79,62 @@ export function createPortableProfilePackService(deps = {}) {
|
|
|
64
79
|
},
|
|
65
80
|
};
|
|
66
81
|
}
|
|
82
|
+
function renderDotEnv(values) {
|
|
83
|
+
return (Object.entries(values)
|
|
84
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
85
|
+
.map(([key, value]) => `${key.toUpperCase()}=${escapeEnvValue(value)}`)
|
|
86
|
+
.join('\n') + '\n');
|
|
87
|
+
}
|
|
88
|
+
function escapeEnvValue(value) {
|
|
89
|
+
if (/[\s"'#$\\]/.test(value)) {
|
|
90
|
+
return `"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
|
|
91
|
+
}
|
|
92
|
+
return value;
|
|
93
|
+
}
|
|
94
|
+
async function writeRepoReadyFiles(stagingDir, packed) {
|
|
95
|
+
const credentials = packed.manifest.credentials ?? [];
|
|
96
|
+
const envLines = credentials.map((c) => `${c.key.toUpperCase()}=${c.description ? `# ${c.description}` : ''}`);
|
|
97
|
+
const envExample = envLines.length > 0
|
|
98
|
+
? `# Credentials required by this profile\n# Copy to .env and fill in values before \`brainctl profile import\`.\n${envLines.join('\n')}\n`
|
|
99
|
+
: '# No credentials required by this profile.\n';
|
|
100
|
+
const gitignore = ['.env', 'node_modules/', '.DS_Store', '*.bak.*', ''].join('\n');
|
|
101
|
+
const readme = renderReadme(packed);
|
|
102
|
+
await writeFile(path.join(stagingDir, '.env.example'), envExample, 'utf8');
|
|
103
|
+
await writeFile(path.join(stagingDir, '.gitignore'), gitignore, 'utf8');
|
|
104
|
+
await writeFile(path.join(stagingDir, 'README.md'), readme, 'utf8');
|
|
105
|
+
}
|
|
106
|
+
function renderReadme(packed) {
|
|
107
|
+
const { manifest, profile } = packed;
|
|
108
|
+
const mcpNames = Object.keys(profile.mcps);
|
|
109
|
+
const credNames = (manifest.credentials ?? []).map((c) => `- \`${c.key}\`${c.description ? ` — ${c.description}` : ''}`);
|
|
110
|
+
const lines = [
|
|
111
|
+
`# ${profile.name}`,
|
|
112
|
+
'',
|
|
113
|
+
profile.description ?? 'A brainctl portable profile.',
|
|
114
|
+
'',
|
|
115
|
+
`Packed by ${manifest.createdBy?.tool ?? 'brainctl'} v${manifest.createdBy?.version ?? packageVersion.version}.`,
|
|
116
|
+
'',
|
|
117
|
+
'## Contents',
|
|
118
|
+
'',
|
|
119
|
+
`- **MCPs:** ${mcpNames.length > 0 ? mcpNames.join(', ') : '(none)'}`,
|
|
120
|
+
...(manifest.plugins ? [`- **Plugins:** ${manifest.plugins.map((p) => `${p.agent}:${p.name}`).join(', ')}`] : []),
|
|
121
|
+
...(manifest.userSkills ? [`- **User skills:** ${manifest.userSkills.map((s) => `${s.agent}:${s.name}`).join(', ')}`] : []),
|
|
122
|
+
'',
|
|
123
|
+
'## Install',
|
|
124
|
+
'',
|
|
125
|
+
'```bash',
|
|
126
|
+
`brainctl profile import ./${profile.name}`,
|
|
127
|
+
'```',
|
|
128
|
+
'',
|
|
129
|
+
];
|
|
130
|
+
if (credNames.length > 0) {
|
|
131
|
+
lines.push('## Required credentials', '', 'Copy `.env.example` to `.env` (gitignored) and fill in values, or pass them at import time:', '', '```bash', `brainctl profile import ./${profile.name} \\`, ...credNames.map((c) => {
|
|
132
|
+
const key = c.match(/`([^`]+)`/)?.[1] ?? '';
|
|
133
|
+
return ` --credential ${key}=<value> \\`;
|
|
134
|
+
}), '```', '', '### Keys', '', ...credNames, '');
|
|
135
|
+
}
|
|
136
|
+
return lines.join('\n');
|
|
137
|
+
}
|
|
67
138
|
async function buildPackedProfile(options) {
|
|
68
139
|
if (options.source.source === 'profile') {
|
|
69
140
|
const profile = await options.profileService.get({
|
|
@@ -117,9 +188,7 @@ async function buildPackedProfile(options) {
|
|
|
117
188
|
const extras = collectAgentExtras(agentConfig);
|
|
118
189
|
return redactAndNormalizeProfile({
|
|
119
190
|
name: profileName,
|
|
120
|
-
skills: {},
|
|
121
191
|
mcps,
|
|
122
|
-
memory: { paths: [] },
|
|
123
192
|
}, agentSource.cwd, {
|
|
124
193
|
kind: 'agent',
|
|
125
194
|
agent: agentSource.agent,
|
|
@@ -186,14 +255,18 @@ function inferPluginVersion(agent, installPath) {
|
|
|
186
255
|
const base = path.basename(installPath);
|
|
187
256
|
return base.length > 0 ? base : undefined;
|
|
188
257
|
}
|
|
189
|
-
function redactAndNormalizeProfile(profile, cwd, source, extras) {
|
|
258
|
+
async function redactAndNormalizeProfile(profile, cwd, source, extras) {
|
|
190
259
|
const bundledSources = new Map();
|
|
191
260
|
const credentials = new Map();
|
|
261
|
+
const rawCredentials = {};
|
|
192
262
|
const mcps = Object.fromEntries(Object.entries(profile.mcps).map(([key, config]) => {
|
|
193
263
|
const result = redactPortableMcpCredentials(config);
|
|
194
264
|
for (const credential of result.credentials) {
|
|
195
265
|
credentials.set(credential.key, credential);
|
|
196
266
|
}
|
|
267
|
+
for (const [credKey, credValue] of Object.entries(result.rawValues)) {
|
|
268
|
+
rawCredentials[credKey] = credValue;
|
|
269
|
+
}
|
|
197
270
|
if (result.redacted.kind === 'local' && result.redacted.source === 'bundled') {
|
|
198
271
|
const sourcePath = path.isAbsolute(result.redacted.path)
|
|
199
272
|
? result.redacted.path
|
|
@@ -230,6 +303,7 @@ function redactAndNormalizeProfile(profile, cwd, source, extras) {
|
|
|
230
303
|
bundledSources,
|
|
231
304
|
bundledPlugins: extras?.bundledPlugins ?? new Map(),
|
|
232
305
|
bundledUserSkills: extras?.bundledUserSkills ?? new Map(),
|
|
306
|
+
rawCredentials,
|
|
233
307
|
};
|
|
234
308
|
}
|
|
235
309
|
function sanitizePackName(value) {
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { AgentName, SyncResult } from '../types.js';
|
|
2
|
+
import { type ProfileSnapshotService } from './profile-snapshot-service.js';
|
|
3
|
+
import { type ProfileService } from './profile-service.js';
|
|
4
|
+
import type { AgentConfigWriter } from './sync/agent-writer.js';
|
|
5
|
+
export type ItemType = 'mcp' | 'plugin' | 'skill';
|
|
6
|
+
export interface ItemSelector {
|
|
7
|
+
type: ItemType;
|
|
8
|
+
name: string;
|
|
9
|
+
}
|
|
10
|
+
export interface ApplyOptions {
|
|
11
|
+
cwd?: string;
|
|
12
|
+
profileName: string;
|
|
13
|
+
agents: AgentName[];
|
|
14
|
+
items?: ItemSelector[];
|
|
15
|
+
backup?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface ApplyResult extends SyncResult {
|
|
18
|
+
}
|
|
19
|
+
export interface ProfileApplyService {
|
|
20
|
+
execute(options: ApplyOptions): Promise<{
|
|
21
|
+
backups: Array<{
|
|
22
|
+
agent: AgentName;
|
|
23
|
+
profileName: string;
|
|
24
|
+
}>;
|
|
25
|
+
applied: ApplyResult;
|
|
26
|
+
}>;
|
|
27
|
+
}
|
|
28
|
+
interface ProfileApplyDependencies {
|
|
29
|
+
profileService?: ProfileService;
|
|
30
|
+
snapshotService?: ProfileSnapshotService;
|
|
31
|
+
writers?: Partial<Record<AgentName, AgentConfigWriter>>;
|
|
32
|
+
}
|
|
33
|
+
export declare function createProfileApplyService(deps?: ProfileApplyDependencies): ProfileApplyService;
|
|
34
|
+
export {};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import YAML from 'yaml';
|
|
4
|
+
import { ProfileError } from '../errors.js';
|
|
5
|
+
import { installPlugin, installUserSkill } from './agent-asset-installer.js';
|
|
6
|
+
import { createProfileSnapshotService, defaultBackupProfileName, } from './profile-snapshot-service.js';
|
|
7
|
+
import { createProfileService, profileDir } from './profile-service.js';
|
|
8
|
+
import { createClaudeWriter } from './sync/claude-writer.js';
|
|
9
|
+
import { createCodexWriter } from './sync/codex-writer.js';
|
|
10
|
+
import { createGeminiWriter } from './sync/gemini-writer.js';
|
|
11
|
+
export function createProfileApplyService(deps = {}) {
|
|
12
|
+
const profileService = deps.profileService ?? createProfileService();
|
|
13
|
+
const snapshotService = deps.snapshotService ?? createProfileSnapshotService();
|
|
14
|
+
const defaultWriters = {
|
|
15
|
+
claude: createClaudeWriter(),
|
|
16
|
+
codex: createCodexWriter(),
|
|
17
|
+
gemini: createGeminiWriter(),
|
|
18
|
+
};
|
|
19
|
+
const writers = { ...defaultWriters, ...deps.writers };
|
|
20
|
+
return {
|
|
21
|
+
async execute(options) {
|
|
22
|
+
const cwd = options.cwd ?? process.cwd();
|
|
23
|
+
const profile = await profileService.get({ cwd, name: options.profileName });
|
|
24
|
+
const remoteMcpName = Object.entries(profile.mcps).find(([, config]) => config.kind === 'remote')?.[0];
|
|
25
|
+
if (remoteMcpName) {
|
|
26
|
+
throw new ProfileError(`Profile "${profile.name}" includes remote MCP "${remoteMcpName}". Remote MCP apply is not supported yet.`);
|
|
27
|
+
}
|
|
28
|
+
const isPartial = options.items !== undefined && options.items.length > 0;
|
|
29
|
+
const shouldBackup = options.backup ?? !isPartial;
|
|
30
|
+
const backups = [];
|
|
31
|
+
if (shouldBackup) {
|
|
32
|
+
for (const agent of options.agents) {
|
|
33
|
+
const backupName = defaultBackupProfileName(agent);
|
|
34
|
+
try {
|
|
35
|
+
await snapshotService.execute({ cwd, agent, profileName: backupName });
|
|
36
|
+
backups.push({ agent, profileName: backupName });
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// Agent may not have a live config to back up — skip silently
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const folder = profileDir(cwd, options.profileName);
|
|
44
|
+
const manifest = await readProfileManifest(folder);
|
|
45
|
+
const applied = [];
|
|
46
|
+
const wantMcp = (name) => matches(options.items, 'mcp', name);
|
|
47
|
+
const wantPlugin = (name) => matches(options.items, 'plugin', name);
|
|
48
|
+
const wantSkill = (name) => matches(options.items, 'skill', name);
|
|
49
|
+
for (const agent of options.agents) {
|
|
50
|
+
const writer = writers[agent];
|
|
51
|
+
if (!writer)
|
|
52
|
+
continue;
|
|
53
|
+
const filteredMcps = Object.fromEntries(Object.entries(profile.mcps).filter(([name]) => wantMcp(name)));
|
|
54
|
+
let mcpResult;
|
|
55
|
+
if (Object.keys(filteredMcps).length > 0 || options.items === undefined) {
|
|
56
|
+
mcpResult = await writer.write({ mcpServers: filteredMcps, cwd });
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
mcpResult = { configPath: '', backedUpTo: null };
|
|
60
|
+
}
|
|
61
|
+
const pluginsInstalled = [];
|
|
62
|
+
for (const plugin of (manifest?.plugins ?? []).filter((p) => p.agent === agent && wantPlugin(p.name))) {
|
|
63
|
+
const sourceDir = path.join(folder, plugin.archivePath);
|
|
64
|
+
await installPlugin(sourceDir, plugin);
|
|
65
|
+
pluginsInstalled.push(plugin.name);
|
|
66
|
+
}
|
|
67
|
+
const userSkillsInstalled = [];
|
|
68
|
+
for (const skill of (manifest?.userSkills ?? []).filter((s) => s.agent === agent && wantSkill(s.name))) {
|
|
69
|
+
const sourceDir = path.join(folder, skill.archivePath);
|
|
70
|
+
await installUserSkill(sourceDir, skill);
|
|
71
|
+
userSkillsInstalled.push(skill.name);
|
|
72
|
+
}
|
|
73
|
+
applied.push({
|
|
74
|
+
agent,
|
|
75
|
+
configPath: mcpResult.configPath,
|
|
76
|
+
backedUpTo: mcpResult.backedUpTo,
|
|
77
|
+
mcpCount: Object.keys(filteredMcps).length,
|
|
78
|
+
...(pluginsInstalled.length > 0 ? { pluginsInstalled } : {}),
|
|
79
|
+
...(userSkillsInstalled.length > 0 ? { userSkillsInstalled } : {}),
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
return { backups, applied };
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function matches(items, type, name) {
|
|
87
|
+
if (items === undefined)
|
|
88
|
+
return true;
|
|
89
|
+
return items.some((s) => s.type === type && s.name === name);
|
|
90
|
+
}
|
|
91
|
+
async function readProfileManifest(folder) {
|
|
92
|
+
try {
|
|
93
|
+
const source = await readFile(path.join(folder, 'manifest.yaml'), 'utf8');
|
|
94
|
+
const parsed = YAML.parse(source);
|
|
95
|
+
if (!parsed || typeof parsed !== 'object')
|
|
96
|
+
return null;
|
|
97
|
+
return parsed;
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AgentName } from '../types.js';
|
|
2
2
|
import type { AgentConfigService } from './agent-config-service.js';
|
|
3
|
-
import { type PortableProfilePackService } from './portable-profile-pack-service.js';
|
|
3
|
+
import { type PortableCredentialsMode, type PortablePackFormat, type PortableProfilePackService } from './portable-profile-pack-service.js';
|
|
4
4
|
import type { ProfileService } from './profile-service.js';
|
|
5
5
|
export interface ProfileExportService {
|
|
6
6
|
execute(options: {
|
|
@@ -14,8 +14,12 @@ export interface ProfileExportService {
|
|
|
14
14
|
cwd: string;
|
|
15
15
|
};
|
|
16
16
|
outputPath?: string;
|
|
17
|
+
format?: PortablePackFormat;
|
|
18
|
+
credentialsMode?: PortableCredentialsMode;
|
|
17
19
|
}): Promise<{
|
|
18
20
|
archivePath: string;
|
|
21
|
+
format: PortablePackFormat;
|
|
22
|
+
warnings: string[];
|
|
19
23
|
}>;
|
|
20
24
|
}
|
|
21
25
|
interface ProfileExportDependencies {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createPortableProfilePackService } from './portable-profile-pack-service.js';
|
|
1
|
+
import { createPortableProfilePackService, } from './portable-profile-pack-service.js';
|
|
2
2
|
export function createProfileExportService(deps = {}) {
|
|
3
3
|
const portableProfilePackService = deps.portableProfilePackService ?? createPortableProfilePackService({
|
|
4
4
|
profileService: deps.profileService,
|
|
@@ -10,6 +10,8 @@ export function createProfileExportService(deps = {}) {
|
|
|
10
10
|
cwd: options.cwd,
|
|
11
11
|
source: options.source,
|
|
12
12
|
outputPath: options.outputPath,
|
|
13
|
+
format: options.format,
|
|
14
|
+
credentialsMode: options.credentialsMode,
|
|
13
15
|
});
|
|
14
16
|
},
|
|
15
17
|
};
|