create-byan-agent 2.11.1 → 2.11.2
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.
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import fsSync from 'node:fs';
|
|
3
|
+
import fsPromises from 'node:fs/promises';
|
|
4
|
+
import nodePath from 'node:path';
|
|
2
5
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
3
6
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
7
|
import {
|
|
@@ -94,6 +97,81 @@ async function apiRequest(path, options = {}) {
|
|
|
94
97
|
return body;
|
|
95
98
|
}
|
|
96
99
|
|
|
100
|
+
// Default filters — skip common build/vcs artifacts that pollute payload.
|
|
101
|
+
const DEFAULT_SKIP_DIRS = new Set([
|
|
102
|
+
'.git', 'node_modules', 'dist', 'build', '.next', 'coverage',
|
|
103
|
+
'__pycache__', '.venv', 'venv', '.pytest_cache', '.mypy_cache',
|
|
104
|
+
'target', 'out', '.turbo', '.cache', '.DS_Store',
|
|
105
|
+
]);
|
|
106
|
+
const DEFAULT_SKIP_FILE_PATTERNS = [
|
|
107
|
+
/\.log$/i, /\.sqlite$/i, /\.sqlite-journal$/i, /\.sqlite-wal$/i,
|
|
108
|
+
/\.lock$/i, /\.pid$/i,
|
|
109
|
+
];
|
|
110
|
+
// Heuristic: treat as binary if content has NUL byte in first 8KB.
|
|
111
|
+
function looksBinary(buf) {
|
|
112
|
+
const sample = buf.subarray(0, Math.min(buf.length, 8192));
|
|
113
|
+
for (const b of sample) if (b === 0) return true;
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Hard limits — match W1's API guards so we fail fast client-side.
|
|
118
|
+
const MAX_FILES = 10000;
|
|
119
|
+
const MAX_TOTAL_BYTES = 100 * 1024 * 1024; // 100 MB
|
|
120
|
+
|
|
121
|
+
async function buildFilesPayload(absRoot, opts = {}) {
|
|
122
|
+
const skipDirs = opts.skipDirs || DEFAULT_SKIP_DIRS;
|
|
123
|
+
const skipPatterns = opts.skipPatterns || DEFAULT_SKIP_FILE_PATTERNS;
|
|
124
|
+
const maxFiles = opts.maxFiles || MAX_FILES;
|
|
125
|
+
const maxBytes = opts.maxBytes || MAX_TOTAL_BYTES;
|
|
126
|
+
|
|
127
|
+
const stat = await fsPromises.stat(absRoot);
|
|
128
|
+
if (!stat.isDirectory()) {
|
|
129
|
+
throw new Error(`Path is not a directory: ${absRoot}`);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const files = [];
|
|
133
|
+
let totalBytes = 0;
|
|
134
|
+
|
|
135
|
+
async function walk(dir) {
|
|
136
|
+
const entries = await fsPromises.readdir(dir, { withFileTypes: true });
|
|
137
|
+
for (const entry of entries) {
|
|
138
|
+
const full = nodePath.join(dir, entry.name);
|
|
139
|
+
if (entry.isDirectory()) {
|
|
140
|
+
if (skipDirs.has(entry.name)) continue;
|
|
141
|
+
await walk(full);
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
if (!entry.isFile()) continue;
|
|
145
|
+
if (skipPatterns.some((re) => re.test(entry.name))) continue;
|
|
146
|
+
|
|
147
|
+
const rel = nodePath.relative(absRoot, full).split(nodePath.sep).join('/');
|
|
148
|
+
const buf = await fsPromises.readFile(full);
|
|
149
|
+
|
|
150
|
+
totalBytes += buf.length;
|
|
151
|
+
if (files.length + 1 > maxFiles) {
|
|
152
|
+
throw new Error(
|
|
153
|
+
`Too many files (>${maxFiles}). Add to skipDirs or increase maxFiles.`
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
if (totalBytes > maxBytes) {
|
|
157
|
+
throw new Error(
|
|
158
|
+
`Total size exceeds ${(maxBytes / 1024 / 1024).toFixed(0)}MB. ` +
|
|
159
|
+
`Prune node_modules/dist/build dirs or increase maxBytes.`
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (looksBinary(buf)) {
|
|
164
|
+
files.push({ path: rel, content: buf.toString('base64'), encoding: 'base64' });
|
|
165
|
+
} else {
|
|
166
|
+
files.push({ path: rel, content: buf.toString('utf8'), encoding: 'utf8' });
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
await walk(absRoot);
|
|
172
|
+
return { files, count: files.length, totalBytes };
|
|
173
|
+
}
|
|
174
|
+
|
|
97
175
|
const tools = [
|
|
98
176
|
{
|
|
99
177
|
name: 'byan_ping',
|
|
@@ -124,13 +202,13 @@ const tools = [
|
|
|
124
202
|
{
|
|
125
203
|
name: 'byan_import_project',
|
|
126
204
|
description:
|
|
127
|
-
'Import a local project directory into byan_web.
|
|
205
|
+
'Import a local project directory into byan_web. Reads files from the local filesystem (client-side) and uploads them as a payload; works whether byan_web is local or remote. Skips .git, node_modules, dist, build, coverage, *.log, *.sqlite. Limits: 10000 files, 100MB total. Requires auth.',
|
|
128
206
|
inputSchema: {
|
|
129
207
|
type: 'object',
|
|
130
208
|
properties: {
|
|
131
209
|
path: {
|
|
132
210
|
type: 'string',
|
|
133
|
-
description: 'Absolute path to the project directory to
|
|
211
|
+
description: 'Absolute path to the project directory on THIS machine (the MCP client). The API does not need filesystem access to this path.',
|
|
134
212
|
},
|
|
135
213
|
name: { type: 'string', description: 'Optional project name override.' },
|
|
136
214
|
type: {
|
|
@@ -138,6 +216,14 @@ const tools = [
|
|
|
138
216
|
enum: ['dev', 'training'],
|
|
139
217
|
description: 'Project type. Default: dev.',
|
|
140
218
|
},
|
|
219
|
+
maxFiles: {
|
|
220
|
+
type: 'number',
|
|
221
|
+
description: 'Override max file count (default 10000).',
|
|
222
|
+
},
|
|
223
|
+
maxBytes: {
|
|
224
|
+
type: 'number',
|
|
225
|
+
description: 'Override max total bytes (default 104857600 = 100MB).',
|
|
226
|
+
},
|
|
141
227
|
},
|
|
142
228
|
required: ['path'],
|
|
143
229
|
additionalProperties: false,
|
|
@@ -820,10 +906,14 @@ const tools = [
|
|
|
820
906
|
{
|
|
821
907
|
name: 'byan_api_import_scan',
|
|
822
908
|
description:
|
|
823
|
-
'Scan a local directory and report what would be imported.
|
|
909
|
+
'Scan a local directory and report what would be imported into byan_web. Reads files from the local filesystem (client-side) and uploads them as a payload; works whether byan_web is local or remote. Skips .git, node_modules, dist, build, coverage, *.log, *.sqlite. Limits: 10000 files, 100MB total. Requires auth.',
|
|
824
910
|
inputSchema: {
|
|
825
911
|
type: 'object',
|
|
826
|
-
properties: {
|
|
912
|
+
properties: {
|
|
913
|
+
path: { type: 'string', description: 'Absolute path to the directory on THIS machine (the MCP client). The API does not need filesystem access to this path.' },
|
|
914
|
+
maxFiles: { type: 'number', description: 'Override max file count (default 10000).' },
|
|
915
|
+
maxBytes: { type: 'number', description: 'Override max total bytes (default 104857600 = 100MB).' },
|
|
916
|
+
},
|
|
827
917
|
required: ['path'],
|
|
828
918
|
additionalProperties: false,
|
|
829
919
|
},
|
|
@@ -831,10 +921,14 @@ const tools = [
|
|
|
831
921
|
{
|
|
832
922
|
name: 'byan_api_import_dry_run',
|
|
833
923
|
description:
|
|
834
|
-
'Dry-run an import from a local directory (no writes).
|
|
924
|
+
'Dry-run an import from a local directory into byan_web (no writes). Reads files from the local filesystem (client-side) and uploads them as a payload; works whether byan_web is local or remote. Skips .git, node_modules, dist, build, coverage, *.log, *.sqlite. Limits: 10000 files, 100MB total. Requires auth.',
|
|
835
925
|
inputSchema: {
|
|
836
926
|
type: 'object',
|
|
837
|
-
properties: {
|
|
927
|
+
properties: {
|
|
928
|
+
path: { type: 'string', description: 'Absolute path to the directory on THIS machine (the MCP client). The API does not need filesystem access to this path.' },
|
|
929
|
+
maxFiles: { type: 'number', description: 'Override max file count (default 10000).' },
|
|
930
|
+
maxBytes: { type: 'number', description: 'Override max total bytes (default 104857600 = 100MB).' },
|
|
931
|
+
},
|
|
838
932
|
required: ['path'],
|
|
839
933
|
additionalProperties: false,
|
|
840
934
|
},
|
|
@@ -899,10 +993,17 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
899
993
|
if (!BYAN_API_TOKEN) {
|
|
900
994
|
throw new Error('BYAN_API_TOKEN env var is required for this tool.');
|
|
901
995
|
}
|
|
996
|
+
// Always upload files payload — works for both localhost and remote API.
|
|
997
|
+
// The API still accepts { path } for backward compat if caller insists,
|
|
998
|
+
// but the MCP client has no reason to use it (we can always read locally).
|
|
999
|
+
const { files } = await buildFilesPayload(args.path, {
|
|
1000
|
+
...(args.maxFiles ? { maxFiles: args.maxFiles } : {}),
|
|
1001
|
+
...(args.maxBytes ? { maxBytes: args.maxBytes } : {}),
|
|
1002
|
+
});
|
|
902
1003
|
const body = await apiRequest('/api/import/project', {
|
|
903
1004
|
method: 'POST',
|
|
904
1005
|
body: JSON.stringify({
|
|
905
|
-
|
|
1006
|
+
files,
|
|
906
1007
|
...(args.name ? { name: args.name } : {}),
|
|
907
1008
|
...(args.type ? { type: args.type } : {}),
|
|
908
1009
|
}),
|
|
@@ -1298,18 +1399,28 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1298
1399
|
|
|
1299
1400
|
if (name === 'byan_api_import_scan') {
|
|
1300
1401
|
requireToken();
|
|
1402
|
+
// Build files payload from client filesystem — works for remote byan_web.
|
|
1403
|
+
const { files } = await buildFilesPayload(args.path, {
|
|
1404
|
+
...(args.maxFiles ? { maxFiles: args.maxFiles } : {}),
|
|
1405
|
+
...(args.maxBytes ? { maxBytes: args.maxBytes } : {}),
|
|
1406
|
+
});
|
|
1301
1407
|
const body = await apiRequest('/api/import/scan', {
|
|
1302
1408
|
method: 'POST',
|
|
1303
|
-
body: JSON.stringify({
|
|
1409
|
+
body: JSON.stringify({ files }),
|
|
1304
1410
|
});
|
|
1305
1411
|
return { content: [{ type: 'text', text: JSON.stringify(body, null, 2) }] };
|
|
1306
1412
|
}
|
|
1307
1413
|
|
|
1308
1414
|
if (name === 'byan_api_import_dry_run') {
|
|
1309
1415
|
requireToken();
|
|
1416
|
+
// Build files payload from client filesystem — works for remote byan_web.
|
|
1417
|
+
const { files } = await buildFilesPayload(args.path, {
|
|
1418
|
+
...(args.maxFiles ? { maxFiles: args.maxFiles } : {}),
|
|
1419
|
+
...(args.maxBytes ? { maxBytes: args.maxBytes } : {}),
|
|
1420
|
+
});
|
|
1310
1421
|
const body = await apiRequest('/api/import/dry-run', {
|
|
1311
1422
|
method: 'POST',
|
|
1312
|
-
body: JSON.stringify({
|
|
1423
|
+
body: JSON.stringify({ files }),
|
|
1313
1424
|
});
|
|
1314
1425
|
return { content: [{ type: 'text', text: JSON.stringify(body, null, 2) }] };
|
|
1315
1426
|
}
|
|
@@ -1325,3 +1436,5 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1325
1436
|
|
|
1326
1437
|
const transport = new StdioServerTransport();
|
|
1327
1438
|
await server.connect(transport);
|
|
1439
|
+
|
|
1440
|
+
export { buildFilesPayload };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-byan-agent",
|
|
3
|
-
"version": "2.11.
|
|
3
|
+
"version": "2.11.2",
|
|
4
4
|
"description": "BYAN v2.8 - Intelligent AI agent creator with ELO trust system + scientific fact-check + Hermes universal dispatcher + native Claude Code integration (hooks, skills, MCP server). Multi-platform (Copilot CLI, Claude Code, Codex). Merise Agile + TDD + 64 Mantras. ~54% LLM cost savings.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|