@bahulam/code 0.1.13 → 0.1.15
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/package.json +1 -1
- package/src/commands/install.mjs +213 -4
- package/src/commands/plugin-manage.mjs +215 -29
- package/src/config/model-catalog-default.json +0 -4
- package/src/core/resume-mode.mjs +0 -1
- package/src/core/stream-client.mjs +18 -0
- package/src/daemon/session-core.mjs +3 -0
- package/src/local-service/agent-relay.mjs +6 -1
- package/src/local-service/file-access.mjs +116 -2
- package/src/local-service/server.mjs +209 -20
- package/src/plugins/npm-install.mjs +138 -0
- package/src/plugins/pi-compat/requirements.mjs +465 -0
- package/src/plugins/pi-compat/scaffold.mjs +86 -10
- package/src/plugins/pi-compat/shim.mjs +29 -1
- package/src/plugins/preflight.mjs +2 -0
- package/src/terminal/main.mjs +5 -2
- package/src/terminal/repl.mjs +52 -23
- package/src/ui/input-dock.mjs +48 -10
- package/src/ui/text-layout.mjs +4 -3
|
@@ -99,10 +99,13 @@ export const DAEMON_OWNED_FIELDS = Object.freeze([
|
|
|
99
99
|
* • It's input state tied to a keyboard (`inputHistory`).
|
|
100
100
|
* • It's a rendering flag whose value depends on the current visible
|
|
101
101
|
* transcript, not the session's actual state (`inSubAgent`).
|
|
102
|
+
* • It's an active renderer collection that does not JSON round-trip
|
|
103
|
+
* safely (`activeSubAgentRuns` is a Map).
|
|
102
104
|
*/
|
|
103
105
|
export const CLIENT_OWNED_SESSION_FIELDS = Object.freeze([
|
|
104
106
|
'inputHistory',
|
|
105
107
|
'inSubAgent',
|
|
108
|
+
'activeSubAgentRuns',
|
|
106
109
|
'creditsLowWarned',
|
|
107
110
|
'msgsLowWarned',
|
|
108
111
|
'_lastEmittedThinking',
|
|
@@ -748,6 +748,8 @@ export class LocalAgentRelay {
|
|
|
748
748
|
lines.push(
|
|
749
749
|
'Use analyze_image(path=..., question=...) for images.',
|
|
750
750
|
'Use read_table(path=...) for CSV/TSV/Excel-style tables.',
|
|
751
|
+
'Use browser/web inspection tools for HTML and browser-rendered web assets when available.',
|
|
752
|
+
'For video and audio, inspect file metadata and ask for a transcript or frame extraction when content analysis is needed.',
|
|
751
753
|
'Use read_attachment(path=...) for text, PDFs, Markdown, JSON/YAML, and Jupyter notebooks.',
|
|
752
754
|
'For unsupported binary files, inspect metadata or ask before attempting lossy conversion.',
|
|
753
755
|
);
|
|
@@ -902,7 +904,10 @@ function attachmentToolHint(file) {
|
|
|
902
904
|
const ext = String(file?.path || file?.name || '').split('.').pop().toLowerCase();
|
|
903
905
|
if (kind === 'image' || mime.startsWith('image/')) return 'analyze_image';
|
|
904
906
|
if (kind === 'spreadsheet' || kind === 'table' || ['csv', 'tsv', 'xlsx', 'xls', 'ods'].includes(ext)) return 'read_table';
|
|
905
|
-
if (
|
|
907
|
+
if (kind === 'web' || ['html', 'htm'].includes(ext)) return 'web_preview';
|
|
908
|
+
if (kind === 'video' || mime.startsWith('video/')) return 'media_metadata';
|
|
909
|
+
if (kind === 'audio' || mime.startsWith('audio/')) return 'media_metadata';
|
|
910
|
+
if (['pdf', 'markdown', 'text', 'code', 'config', 'notebook'].includes(kind) || ['txt', 'md', 'mdx', 'pdf', 'json', 'yaml', 'yml', 'toml', 'xml', 'ipynb', 'log', 'rst', 'sql', 'sh'].includes(ext)) return 'read_attachment';
|
|
906
911
|
return '';
|
|
907
912
|
}
|
|
908
913
|
|
|
@@ -6,6 +6,7 @@ import * as fs from 'node:fs';
|
|
|
6
6
|
import * as path from 'node:path';
|
|
7
7
|
|
|
8
8
|
const DEFAULT_MAX_ENTRIES = 300;
|
|
9
|
+
const DEFAULT_MAX_SEARCH_RESULTS = 200;
|
|
9
10
|
const DEFAULT_MAX_TEXT_BYTES = 256 * 1024;
|
|
10
11
|
export const DEFAULT_MAX_RAW_BYTES = 100 * 1024 * 1024;
|
|
11
12
|
|
|
@@ -42,8 +43,8 @@ const EXTENSION_FILE_TYPES = new Map([
|
|
|
42
43
|
['env', { kind: 'config', language: 'plaintext', label: 'Environment', textLike: true }],
|
|
43
44
|
['properties', { kind: 'config', language: 'plaintext', label: 'Properties', textLike: true }],
|
|
44
45
|
['xml', { kind: 'code', language: 'xml', label: 'XML', textLike: true }],
|
|
45
|
-
['html', { kind: '
|
|
46
|
-
['htm', { kind: '
|
|
46
|
+
['html', { kind: 'web', language: 'html', label: 'HTML', textLike: true }],
|
|
47
|
+
['htm', { kind: 'web', language: 'html', label: 'HTML', textLike: true }],
|
|
47
48
|
['css', { kind: 'code', language: 'css', label: 'CSS', textLike: true }],
|
|
48
49
|
['scss', { kind: 'code', language: 'scss', label: 'SCSS', textLike: true }],
|
|
49
50
|
['sass', { kind: 'code', language: 'scss', label: 'Sass', textLike: true }],
|
|
@@ -110,6 +111,18 @@ const EXTENSION_FILE_TYPES = new Map([
|
|
|
110
111
|
['svg', { kind: 'image', language: 'xml', label: 'SVG Image', textLike: false }],
|
|
111
112
|
['bmp', { kind: 'image', language: null, label: 'Bitmap Image', textLike: false }],
|
|
112
113
|
['ico', { kind: 'image', language: null, label: 'Icon', textLike: false }],
|
|
114
|
+
['avif', { kind: 'image', language: null, label: 'AVIF Image', textLike: false }],
|
|
115
|
+
['mp4', { kind: 'video', language: null, label: 'MP4 Video', textLike: false }],
|
|
116
|
+
['webm', { kind: 'video', language: null, label: 'WebM Video', textLike: false }],
|
|
117
|
+
['mov', { kind: 'video', language: null, label: 'QuickTime Video', textLike: false }],
|
|
118
|
+
['m4v', { kind: 'video', language: null, label: 'M4V Video', textLike: false }],
|
|
119
|
+
['ogv', { kind: 'video', language: null, label: 'Ogg Video', textLike: false }],
|
|
120
|
+
['mp3', { kind: 'audio', language: null, label: 'MP3 Audio', textLike: false }],
|
|
121
|
+
['m4a', { kind: 'audio', language: null, label: 'M4A Audio', textLike: false }],
|
|
122
|
+
['wav', { kind: 'audio', language: null, label: 'WAV Audio', textLike: false }],
|
|
123
|
+
['ogg', { kind: 'audio', language: null, label: 'Ogg Audio', textLike: false }],
|
|
124
|
+
['flac', { kind: 'audio', language: null, label: 'FLAC Audio', textLike: false }],
|
|
125
|
+
['aac', { kind: 'audio', language: null, label: 'AAC Audio', textLike: false }],
|
|
113
126
|
['pdf', { kind: 'pdf', language: null, label: 'PDF', textLike: false }],
|
|
114
127
|
['xlsx', { kind: 'spreadsheet', language: null, label: 'Excel Workbook', textLike: false }],
|
|
115
128
|
['xls', { kind: 'spreadsheet', language: null, label: 'Excel Workbook', textLike: false }],
|
|
@@ -165,12 +178,16 @@ export function listWorkspacePath(session, requestedPath = '.', { maxEntries = D
|
|
|
165
178
|
try {
|
|
166
179
|
childStat = fs.statSync(fullPath);
|
|
167
180
|
} catch {}
|
|
181
|
+
const file = childStat?.isFile() ? describeFile(root, fullPath, childStat) : null;
|
|
168
182
|
return {
|
|
169
183
|
name: entry.name,
|
|
170
184
|
path: normalizeRelative(root, fullPath),
|
|
171
185
|
type: entry.isDirectory() ? 'directory' : entry.isFile() ? 'file' : 'other',
|
|
172
186
|
size: childStat?.size ?? null,
|
|
173
187
|
updated_at: childStat?.mtime ? childStat.mtime.toISOString() : null,
|
|
188
|
+
kind: file?.kind ?? (entry.isDirectory() ? 'directory' : null),
|
|
189
|
+
viewer: file?.viewer ?? (entry.isDirectory() ? 'directory' : null),
|
|
190
|
+
label: file?.label ?? null,
|
|
174
191
|
};
|
|
175
192
|
});
|
|
176
193
|
|
|
@@ -201,6 +218,88 @@ export function listWorkspacePath(session, requestedPath = '.', { maxEntries = D
|
|
|
201
218
|
};
|
|
202
219
|
}
|
|
203
220
|
|
|
221
|
+
export function searchWorkspaceFiles(session, {
|
|
222
|
+
query = '',
|
|
223
|
+
kinds = [],
|
|
224
|
+
maxResults = DEFAULT_MAX_SEARCH_RESULTS,
|
|
225
|
+
} = {}) {
|
|
226
|
+
const root = fs.realpathSync(session.root_path);
|
|
227
|
+
const wanted = new Set((Array.isArray(kinds) ? kinds : String(kinds || '').split(','))
|
|
228
|
+
.map((kind) => String(kind || '').trim().toLowerCase())
|
|
229
|
+
.filter(Boolean)
|
|
230
|
+
.filter((kind) => kind !== 'all'));
|
|
231
|
+
const term = String(query || '').trim().toLowerCase();
|
|
232
|
+
const limit = Math.max(1, Math.min(1000, Number(maxResults) || DEFAULT_MAX_SEARCH_RESULTS));
|
|
233
|
+
const results = [];
|
|
234
|
+
|
|
235
|
+
function visit(dir) {
|
|
236
|
+
if (results.length >= limit) return;
|
|
237
|
+
let entries = [];
|
|
238
|
+
try {
|
|
239
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
240
|
+
} catch {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
entries.sort((a, b) => {
|
|
244
|
+
if (a.isDirectory() !== b.isDirectory()) return a.isDirectory() ? -1 : 1;
|
|
245
|
+
return a.name.localeCompare(b.name);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
for (const entry of entries) {
|
|
249
|
+
if (results.length >= limit) break;
|
|
250
|
+
if (shouldHideEntry(entry.name)) continue;
|
|
251
|
+
const fullPath = path.join(dir, entry.name);
|
|
252
|
+
let stat = null;
|
|
253
|
+
try {
|
|
254
|
+
stat = fs.statSync(fullPath);
|
|
255
|
+
} catch {
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
const rel = normalizeRelative(root, fullPath);
|
|
259
|
+
if (entry.isDirectory()) {
|
|
260
|
+
if ((!term || entry.name.toLowerCase().includes(term) || rel.toLowerCase().includes(term))
|
|
261
|
+
&& (!wanted.size || wanted.has('directory'))) {
|
|
262
|
+
results.push({
|
|
263
|
+
name: entry.name,
|
|
264
|
+
path: rel,
|
|
265
|
+
type: 'directory',
|
|
266
|
+
size: null,
|
|
267
|
+
updated_at: stat.mtime.toISOString(),
|
|
268
|
+
kind: 'directory',
|
|
269
|
+
viewer: 'directory',
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
visit(fullPath);
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
if (!entry.isFile()) continue;
|
|
276
|
+
const file = describeFile(root, fullPath, stat);
|
|
277
|
+
if (wanted.size && !wanted.has(file.kind) && !wanted.has(file.viewer)) continue;
|
|
278
|
+
if (term && !entry.name.toLowerCase().includes(term) && !rel.toLowerCase().includes(term)) continue;
|
|
279
|
+
results.push({
|
|
280
|
+
name: entry.name,
|
|
281
|
+
path: rel,
|
|
282
|
+
type: 'file',
|
|
283
|
+
size: stat.size,
|
|
284
|
+
updated_at: stat.mtime.toISOString(),
|
|
285
|
+
kind: file.kind,
|
|
286
|
+
viewer: file.viewer,
|
|
287
|
+
label: file.label,
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
visit(root);
|
|
293
|
+
return {
|
|
294
|
+
ok: true,
|
|
295
|
+
type: 'search',
|
|
296
|
+
query: term,
|
|
297
|
+
kinds: [...wanted],
|
|
298
|
+
results,
|
|
299
|
+
truncated: results.length >= limit,
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
|
|
204
303
|
export function readWorkspaceFile(session, requestedPath, { maxBytes = DEFAULT_MAX_TEXT_BYTES } = {}) {
|
|
205
304
|
const target = resolveWorkspacePath(session, requestedPath);
|
|
206
305
|
const stat = fs.statSync(target);
|
|
@@ -252,6 +351,18 @@ export function contentTypeForPath(filePath) {
|
|
|
252
351
|
case 'svg': return 'image/svg+xml';
|
|
253
352
|
case 'bmp': return 'image/bmp';
|
|
254
353
|
case 'ico': return 'image/x-icon';
|
|
354
|
+
case 'avif': return 'image/avif';
|
|
355
|
+
case 'mp4': return 'video/mp4';
|
|
356
|
+
case 'webm': return 'video/webm';
|
|
357
|
+
case 'mov': return 'video/quicktime';
|
|
358
|
+
case 'm4v': return 'video/x-m4v';
|
|
359
|
+
case 'ogv': return 'video/ogg';
|
|
360
|
+
case 'mp3': return 'audio/mpeg';
|
|
361
|
+
case 'm4a': return 'audio/mp4';
|
|
362
|
+
case 'wav': return 'audio/wav';
|
|
363
|
+
case 'ogg': return 'audio/ogg';
|
|
364
|
+
case 'flac': return 'audio/flac';
|
|
365
|
+
case 'aac': return 'audio/aac';
|
|
255
366
|
case 'pdf': return 'application/pdf';
|
|
256
367
|
case 'txt':
|
|
257
368
|
case 'log':
|
|
@@ -351,7 +462,10 @@ function viewerForKind(kind) {
|
|
|
351
462
|
case 'mermaid': return 'mermaid';
|
|
352
463
|
case 'drawio': return 'drawio';
|
|
353
464
|
case 'image': return 'image';
|
|
465
|
+
case 'video': return 'video';
|
|
466
|
+
case 'audio': return 'audio';
|
|
354
467
|
case 'pdf': return 'pdf';
|
|
468
|
+
case 'web': return 'web';
|
|
355
469
|
case 'spreadsheet': return 'spreadsheet';
|
|
356
470
|
case 'document': return 'document';
|
|
357
471
|
case 'presentation': return 'presentation';
|