@bahulam/code 0.1.12 → 0.1.14
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 +382 -0
- package/src/commands/plugin-manage.mjs +467 -89
- package/src/config/model-catalog-default.json +0 -4
- package/src/config/settings-loader.mjs +15 -0
- package/src/core/resume-mode.mjs +0 -1
- package/src/core/stream-client.mjs +18 -0
- package/src/core/tool-executor.mjs +47 -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/manifest.mjs +3 -0
- package/src/plugins/npm-install.mjs +138 -0
- package/src/plugins/pi-compat/loader-hook.mjs +45 -0
- package/src/plugins/pi-compat/probe.mjs +294 -0
- package/src/plugins/pi-compat/requirements.mjs +465 -0
- package/src/plugins/pi-compat/scaffold.mjs +563 -0
- package/src/plugins/pi-compat/shim.mjs +162 -0
- package/src/plugins/pi-compose.mjs +147 -0
- package/src/plugins/preflight.mjs +29 -2
- package/src/plugins/registry.mjs +6 -0
- package/src/terminal/main.mjs +33 -6
- package/src/terminal/paste-input.mjs +23 -0
- package/src/terminal/repl.mjs +104 -30
- package/src/tools/registry.mjs +19 -0
- package/src/ui/input-dock.mjs +53 -12
- package/src/ui/text-layout.mjs +4 -3
|
@@ -19,8 +19,8 @@ import {
|
|
|
19
19
|
DEFAULT_MAX_RAW_BYTES,
|
|
20
20
|
contentTypeForPath,
|
|
21
21
|
listWorkspacePath,
|
|
22
|
-
readWorkspaceFile,
|
|
23
22
|
resolveWorkspacePath,
|
|
23
|
+
searchWorkspaceFiles,
|
|
24
24
|
} from './file-access.mjs';
|
|
25
25
|
import {
|
|
26
26
|
createOfficePdfPreview,
|
|
@@ -45,6 +45,7 @@ const DEFAULT_MAX_UPLOAD_FILES = 20;
|
|
|
45
45
|
const DEFAULT_MAX_UPLOAD_FILE_BYTES = DEFAULT_MAX_RAW_BYTES;
|
|
46
46
|
const DEFAULT_MAX_UPLOAD_BODY_BYTES = 140 * 1024 * 1024;
|
|
47
47
|
const DEFAULT_MAX_SAVE_BYTES = 2 * 1024 * 1024;
|
|
48
|
+
const DEFAULT_MAX_MEDIA_BYTES = 1024 * 1024 * 1024;
|
|
48
49
|
|
|
49
50
|
export async function startLocalWorkspaceService({
|
|
50
51
|
session,
|
|
@@ -364,6 +365,19 @@ async function routeRequest({ req, res, sessionId, token, events, sseClients, em
|
|
|
364
365
|
return;
|
|
365
366
|
}
|
|
366
367
|
|
|
368
|
+
if (req.method === 'GET' && url.pathname === '/api/workspace/search') {
|
|
369
|
+
const query = url.searchParams.get('q') || '';
|
|
370
|
+
const kinds = (url.searchParams.get('kind') || url.searchParams.get('kinds') || '')
|
|
371
|
+
.split(',')
|
|
372
|
+
.map((item) => item.trim())
|
|
373
|
+
.filter(Boolean);
|
|
374
|
+
const maxResults = envInt('BAHULAM_LOCAL_SEARCH_MAX_RESULTS', 200);
|
|
375
|
+
const result = searchWorkspaceFiles(session, { query, kinds, maxResults });
|
|
376
|
+
emit('workspace_searched', { query: result.query, kinds: result.kinds, count: result.results.length });
|
|
377
|
+
sendJson(res, 200, result);
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
|
|
367
381
|
if (req.method === 'POST' && url.pathname === '/api/files/upload') {
|
|
368
382
|
const body = await readJsonBody(req, envInt('BAHULAM_LOCAL_UPLOAD_MAX_BODY_BYTES', DEFAULT_MAX_UPLOAD_BODY_BYTES));
|
|
369
383
|
const result = saveUploadedFiles(session, body);
|
|
@@ -402,15 +416,8 @@ async function routeRequest({ req, res, sessionId, token, events, sseClients, em
|
|
|
402
416
|
err.code = 'BAD_REQUEST';
|
|
403
417
|
throw err;
|
|
404
418
|
}
|
|
405
|
-
|
|
406
|
-
emit('file_read', { path: requestedPath,
|
|
407
|
-
res.writeHead(200, {
|
|
408
|
-
'Content-Type': contentTypeForPath(requestedPath),
|
|
409
|
-
'Cache-Control': 'no-store',
|
|
410
|
-
'Content-Security-Policy': "default-src 'none'; style-src 'unsafe-inline'; img-src data: blob:",
|
|
411
|
-
'X-Content-Type-Options': 'nosniff',
|
|
412
|
-
});
|
|
413
|
-
res.end(bytes);
|
|
419
|
+
sendWorkspaceRawFile({ req, res, session, requestedPath });
|
|
420
|
+
emit('file_read', { path: requestedPath, streamed: true });
|
|
414
421
|
return;
|
|
415
422
|
}
|
|
416
423
|
|
|
@@ -691,7 +698,9 @@ const PLUGIN_VIEW_MIME = {
|
|
|
691
698
|
js: 'text/javascript; charset=utf-8', mjs: 'text/javascript; charset=utf-8',
|
|
692
699
|
json: 'application/json', map: 'application/json',
|
|
693
700
|
png: 'image/png', jpg: 'image/jpeg', jpeg: 'image/jpeg', gif: 'image/gif',
|
|
694
|
-
webp: 'image/webp', svg: 'image/svg+xml', ico: 'image/x-icon',
|
|
701
|
+
webp: 'image/webp', svg: 'image/svg+xml', ico: 'image/x-icon', avif: 'image/avif',
|
|
702
|
+
mp4: 'video/mp4', webm: 'video/webm', mov: 'video/quicktime', m4v: 'video/x-m4v', ogv: 'video/ogg',
|
|
703
|
+
mp3: 'audio/mpeg', m4a: 'audio/mp4', wav: 'audio/wav', ogg: 'audio/ogg', flac: 'audio/flac', aac: 'audio/aac',
|
|
695
704
|
woff: 'font/woff', woff2: 'font/woff2', ttf: 'font/ttf',
|
|
696
705
|
wasm: 'application/wasm',
|
|
697
706
|
};
|
|
@@ -801,8 +810,7 @@ function saveUploadedFiles(session, body = {}) {
|
|
|
801
810
|
}
|
|
802
811
|
|
|
803
812
|
const root = fs.realpathSync(session.root_path);
|
|
804
|
-
const
|
|
805
|
-
const directory = path.posix.join(LOCAL_UPLOAD_DIR, safePathSegment(session.id), group);
|
|
813
|
+
const directory = LOCAL_UPLOAD_DIR;
|
|
806
814
|
const uploadDir = path.join(root, ...directory.split('/'));
|
|
807
815
|
fs.mkdirSync(uploadDir, { recursive: true });
|
|
808
816
|
|
|
@@ -880,6 +888,72 @@ function uniqueUploadTarget(uploadDir, fileName) {
|
|
|
880
888
|
return path.join(uploadDir, `${parsed.name}-${crypto.randomBytes(4).toString('hex')}${parsed.ext}`);
|
|
881
889
|
}
|
|
882
890
|
|
|
891
|
+
function sendWorkspaceRawFile({ req, res, session, requestedPath }) {
|
|
892
|
+
const target = resolveWorkspacePath(session, requestedPath);
|
|
893
|
+
const stat = fs.statSync(target);
|
|
894
|
+
if (!stat.isFile()) {
|
|
895
|
+
const err = new Error('Requested path is not a file');
|
|
896
|
+
err.code = 'NOT_FILE';
|
|
897
|
+
throw err;
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
const contentType = contentTypeForPath(target);
|
|
901
|
+
const mediaLike = /^(audio|video)\//.test(contentType);
|
|
902
|
+
const maxBytes = mediaLike
|
|
903
|
+
? envInt('BAHULAM_LOCAL_MEDIA_MAX_BYTES', DEFAULT_MAX_MEDIA_BYTES)
|
|
904
|
+
: DEFAULT_MAX_RAW_BYTES;
|
|
905
|
+
if (stat.size > maxBytes) {
|
|
906
|
+
const err = new Error(`File is larger than ${maxBytes} bytes`);
|
|
907
|
+
err.code = 'FILE_TOO_LARGE';
|
|
908
|
+
throw err;
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
const commonHeaders = {
|
|
912
|
+
'Content-Type': contentType,
|
|
913
|
+
'Cache-Control': 'no-store',
|
|
914
|
+
'Content-Security-Policy': "default-src 'none'; style-src 'unsafe-inline'; img-src data: blob:; media-src 'self' blob:",
|
|
915
|
+
'X-Content-Type-Options': 'nosniff',
|
|
916
|
+
};
|
|
917
|
+
const range = parseRangeHeader(req.headers.range, stat.size);
|
|
918
|
+
if (range) {
|
|
919
|
+
res.writeHead(206, {
|
|
920
|
+
...commonHeaders,
|
|
921
|
+
'Accept-Ranges': 'bytes',
|
|
922
|
+
'Content-Range': `bytes ${range.start}-${range.end}/${stat.size}`,
|
|
923
|
+
'Content-Length': range.end - range.start + 1,
|
|
924
|
+
});
|
|
925
|
+
fs.createReadStream(target, { start: range.start, end: range.end }).pipe(res);
|
|
926
|
+
return;
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
res.writeHead(200, {
|
|
930
|
+
...commonHeaders,
|
|
931
|
+
'Accept-Ranges': mediaLike ? 'bytes' : 'none',
|
|
932
|
+
'Content-Length': stat.size,
|
|
933
|
+
});
|
|
934
|
+
fs.createReadStream(target).pipe(res);
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
function parseRangeHeader(header, size) {
|
|
938
|
+
const value = String(header || '').trim();
|
|
939
|
+
if (!value || !value.startsWith('bytes=')) return null;
|
|
940
|
+
const first = value.slice(6).split(',')[0]?.trim() || '';
|
|
941
|
+
const match = first.match(/^(\d*)-(\d*)$/);
|
|
942
|
+
if (!match) return null;
|
|
943
|
+
let start = match[1] ? Number(match[1]) : null;
|
|
944
|
+
let end = match[2] ? Number(match[2]) : null;
|
|
945
|
+
if (start == null && end == null) return null;
|
|
946
|
+
if (start == null) {
|
|
947
|
+
const suffix = Math.max(0, Math.min(size, end || 0));
|
|
948
|
+
start = Math.max(0, size - suffix);
|
|
949
|
+
end = size - 1;
|
|
950
|
+
} else {
|
|
951
|
+
end = end == null ? size - 1 : Math.min(end, size - 1);
|
|
952
|
+
}
|
|
953
|
+
if (!Number.isFinite(start) || !Number.isFinite(end) || start < 0 || end < start || start >= size) return null;
|
|
954
|
+
return { start, end };
|
|
955
|
+
}
|
|
956
|
+
|
|
883
957
|
function sendJson(res, status, body) {
|
|
884
958
|
res.writeHead(status, {
|
|
885
959
|
'Content-Type': 'application/json; charset=utf-8',
|
|
@@ -981,6 +1055,18 @@ function staticContentType(filePath) {
|
|
|
981
1055
|
case '.jpeg': return 'image/jpeg';
|
|
982
1056
|
case '.gif': return 'image/gif';
|
|
983
1057
|
case '.webp': return 'image/webp';
|
|
1058
|
+
case '.avif': return 'image/avif';
|
|
1059
|
+
case '.mp4': return 'video/mp4';
|
|
1060
|
+
case '.webm': return 'video/webm';
|
|
1061
|
+
case '.mov': return 'video/quicktime';
|
|
1062
|
+
case '.m4v': return 'video/x-m4v';
|
|
1063
|
+
case '.ogv': return 'video/ogg';
|
|
1064
|
+
case '.mp3': return 'audio/mpeg';
|
|
1065
|
+
case '.m4a': return 'audio/mp4';
|
|
1066
|
+
case '.wav': return 'audio/wav';
|
|
1067
|
+
case '.ogg': return 'audio/ogg';
|
|
1068
|
+
case '.flac': return 'audio/flac';
|
|
1069
|
+
case '.aac': return 'audio/aac';
|
|
984
1070
|
case '.ttf': return 'font/ttf';
|
|
985
1071
|
case '.woff': return 'font/woff';
|
|
986
1072
|
case '.woff2': return 'font/woff2';
|
|
@@ -1070,11 +1156,11 @@ button,textarea,input{font:inherit}button{color:inherit}.shell{display:flex;flex
|
|
|
1070
1156
|
.menubar{height:28px;display:flex;align-items:center;gap:2px;border-bottom:1px solid rgba(27,27,27,.06);background:#F7F5EF;padding:0 12px;flex:0 0 auto;user-select:none;position:relative;z-index:20}.menu-item{height:22px;border:0;background:transparent;border-radius:var(--ws-radius-sm);padding:0 8px;color:rgba(27,27,27,.58);font-size:12px;font-weight:500;cursor:pointer}.menu-item:hover{background:var(--ws-hover);color:rgba(27,27,27,.86)}.menubar-spacer{flex:1}.session-menu-wrap{display:flex;align-items:center}.session-menu-button{max-width:240px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.session-modal-backdrop,.approval-modal-backdrop{position:fixed;inset:0;z-index:80;display:flex;align-items:flex-start;justify-content:center;background:rgba(27,27,27,.28);padding:92px 16px 24px}.approval-modal-backdrop{z-index:90;background:rgba(27,27,27,.36)}.session-modal-backdrop[hidden],.approval-modal-backdrop[hidden]{display:none}.session-dialog,.approval-dialog{width:min(620px,100%);max-height:min(620px,calc(100vh - 128px));display:flex;flex-direction:column;overflow:hidden;border:1px solid rgba(27,27,27,.12);border-radius:var(--ws-radius-md);background:#FFFDF7;box-shadow:0 24px 70px rgba(27,27,27,.22)}.approval-dialog{width:min(680px,100%)}.session-dialog-head,.approval-dialog-head{height:48px;display:flex;align-items:center;justify-content:space-between;gap:12px;border-bottom:1px solid var(--ws-border-subtle);padding:0 12px}.session-dialog-title,.approval-dialog-title{font-size:13px;font-weight:600;color:rgba(27,27,27,.84)}.session-dialog-subtitle,.approval-dialog-subtitle{margin-top:1px;font:12px/1.45 var(--sans);color:rgba(27,27,27,.46);max-width:560px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.session-menu{display:block;overflow:auto;padding:6px}.session-menu-empty{padding:10px;color:rgba(27,27,27,.42);font-size:12px}.approval-body{padding:12px;overflow:auto}.approval-subject{border:1px solid var(--ws-border);border-radius:var(--ws-radius-md);background:#fff;padding:10px;font:12px/1.45 var(--mono);color:rgba(27,27,27,.72);white-space:pre-wrap;overflow-wrap:anywhere}.approval-reason{margin-top:10px;color:rgba(27,27,27,.58);font-size:12px;line-height:1.5}.approval-actions{display:flex;align-items:center;justify-content:flex-end;gap:8px;border-top:1px solid var(--ws-border-subtle);padding:10px 12px;background:#F7F5EF}.approval-actions .danger{border-color:rgba(185,28,28,.24);color:#B91C1C}.approval-actions .primary-action{background:#1B1B1B;color:#FFFDF7;border-color:#1B1B1B}.approval-inline{width:100%;max-width:960px;border:1px solid rgba(217,119,6,.22);border-radius:var(--ws-radius-md);background:#FFFBEB;overflow:hidden;box-shadow:0 8px 24px rgba(146,64,14,.08)}.approval-inline.done{border-color:rgba(27,27,27,.08);background:#F7F5EF;box-shadow:none}.approval-inline-head{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:9px 11px;border-bottom:1px solid rgba(217,119,6,.14)}.approval-inline-title{font-size:13px;font-weight:600;color:#92400E}.approval-inline-meta{font:11px var(--sans);color:rgba(146,64,14,.62);white-space:nowrap}.approval-inline-subject{margin:10px 11px 0;border:1px solid rgba(217,119,6,.18);border-radius:var(--ws-radius-md);background:#fff;padding:9px;font:11px/1.45 var(--mono);color:rgba(27,27,27,.74);white-space:pre-wrap;overflow-wrap:anywhere;max-height:180px;overflow:auto}.approval-inline-reason{padding:8px 11px 10px;color:rgba(27,27,27,.58);font-size:12px;line-height:1.45}.approval-inline-actions{display:flex;justify-content:flex-end;gap:8px;flex-wrap:wrap;padding:9px 11px;border-top:1px solid rgba(217,119,6,.12);background:rgba(255,255,255,.58)}
|
|
1071
1157
|
main{--left-w:256px;--right-w:480px;display:grid;grid-template-columns:var(--left-w) 6px minmax(360px,1fr) 6px var(--right-w);flex:1;min-height:0}.panel{min-width:0;background:var(--ws-panel);overflow:hidden}.files{grid-column:1;background:rgba(255,255,255,.52)}.left-resizer{grid-column:2}.work{grid-column:3;display:flex;flex-direction:column;background:#fff}.right-resizer{grid-column:4}.agent{grid-column:5;background:rgba(255,255,255,.58);display:flex;flex-direction:column}.resizer{background:transparent;border-left:1px solid var(--ws-border-subtle);border-right:1px solid transparent;cursor:col-resize;position:relative}.resizer:hover,.resizer.dragging{background:rgba(8,145,178,.08);border-left-color:rgba(8,145,178,.25)}
|
|
1072
1158
|
main.hide-files{grid-template-columns:0 0 minmax(360px,1fr) 6px var(--right-w)}main.hide-files .files,main.hide-files .left-resizer{display:none}main.hide-agent{grid-template-columns:var(--left-w) 6px minmax(360px,1fr) 0 0}main.hide-agent .agent,main.hide-agent .right-resizer{display:none}
|
|
1073
|
-
.section-head{height:36px;border-bottom:1px solid var(--ws-border-subtle);display:flex;align-items:center;justify-content:space-between;padding:0 12px;color:var(--ws-faint);font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.08em;background:rgba(255,255,255,.45)}.section-actions{display:flex;align-items:center;gap:4px}.section-button{height:20px;border:0;background:transparent;border-radius:var(--ws-radius-sm);color:rgba(27,27,27,.35);cursor:pointer;padding:0 4px}.section-button:hover{background:var(--ws-hover);color:rgba(27,27,27,.70)}
|
|
1074
|
-
.file-list{padding:6px;overflow:auto;height:calc(100% -
|
|
1159
|
+
.section-head{height:36px;border-bottom:1px solid var(--ws-border-subtle);display:flex;align-items:center;justify-content:space-between;padding:0 12px;color:var(--ws-faint);font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.08em;background:rgba(255,255,255,.45)}.section-actions{display:flex;align-items:center;gap:4px}.section-button{height:20px;border:0;background:transparent;border-radius:var(--ws-radius-sm);color:rgba(27,27,27,.35);cursor:pointer;padding:0 4px}.section-button:hover{background:var(--ws-hover);color:rgba(27,27,27,.70)}.workspace-tools{height:68px;border-bottom:1px solid var(--ws-border-subtle);padding:7px 8px;background:#FFFDF7;display:flex;flex-direction:column;gap:7px}.file-search{width:100%;height:26px;border:1px solid rgba(27,27,27,.10);border-radius:6px;background:#fff;color:var(--ws-foreground);outline:none;padding:0 8px;font-size:12px}.file-search:focus{border-color:rgba(8,145,178,.35);box-shadow:0 0 0 2px rgba(8,145,178,.10)}.file-filters{display:flex;gap:5px;overflow-x:auto;scrollbar-width:none}.file-filters::-webkit-scrollbar{display:none}.filter-chip{height:22px;border:1px solid rgba(27,27,27,.08);border-radius:999px;background:#fff;color:rgba(27,27,27,.52);padding:0 8px;font-size:11px;cursor:pointer;white-space:nowrap}.filter-chip:hover{background:#F7F5EF;color:rgba(27,27,27,.78)}.filter-chip.active{background:#ECFEFF;border-color:rgba(8,145,178,.22);color:#0E7490}.search-results{padding:4px 8px;color:rgba(27,27,27,.38);font:11px var(--sans)}
|
|
1160
|
+
.file-list{padding:6px;overflow:auto;height:calc(100% - 104px)}.row{display:flex;align-items:center;gap:5px;width:100%;height:27px;border:0;background:transparent;text-align:left;border-radius:var(--ws-radius-sm);padding:0 8px;color:rgba(27,27,27,.62);cursor:pointer;font-size:13px;line-height:1}.row:hover{background:var(--ws-hover);color:rgba(27,27,27,.84)}.row.selected{background:var(--ws-active);color:var(--ws-foreground)}.row:focus-visible{outline:2px solid rgba(8,145,178,.35);outline-offset:1px}.chev{width:12px;text-align:center;color:rgba(27,27,27,.32);font:11px var(--mono)}.icon{width:16px;display:inline-flex;align-items:center;justify-content:center;color:var(--ws-faint);font-size:11px;font-family:var(--mono)}.name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sub{color:rgba(27,27,27,.34);font:11px var(--sans);margin-left:auto}.loading-row{height:24px;padding-left:32px;color:rgba(27,27,27,.38);font:12px var(--sans)}
|
|
1075
1161
|
.tabbar{height:36px;display:flex;align-items:center;border-bottom:1px solid var(--ws-border-subtle);background:var(--ws-tab);flex:0 0 auto;overflow-x:auto}.tab{height:100%;display:flex;align-items:center;gap:7px;border:0;border-right:1px solid var(--ws-border-subtle);padding:0 9px;background:transparent;color:rgba(27,27,27,.48);font-size:13px;font-weight:500;cursor:pointer;max-width:220px;min-width:90px}.tab.active{background:#fff;color:var(--ws-foreground);font-weight:600;box-shadow:inset 0 -1.5px 0 var(--ws-primary)}.tab:hover{background:rgba(27,27,27,.03);color:rgba(27,27,27,.75)}.tab-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tab-close{border:0;background:transparent;border-radius:var(--ws-radius-sm);color:rgba(27,27,27,.28);cursor:pointer;padding:0 2px}.tab-close:hover{background:rgba(27,27,27,.07);color:rgba(27,27,27,.70)}.tab-muted{flex:1;height:100%;border-left:1px solid var(--ws-border-subtle);min-width:24px}
|
|
1076
1162
|
.viewer{flex:1;min-height:0;overflow:hidden;background:#fff;display:flex;flex-direction:column}.file-header{min-height:34px;flex:0 0 auto;border-bottom:1px solid var(--ws-border-subtle);display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:5px 12px;background:rgba(27,27,27,.015);font:12px var(--mono);color:rgba(27,27,27,.50)}.path{min-width:120px;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.file-chip{display:inline-flex;align-items:center;gap:5px;border:1px solid rgba(27,27,27,.08);border-radius:999px;background:#fff;padding:1px 7px;font:10px var(--sans);color:rgba(27,27,27,.48);white-space:nowrap}.file-chip.warn{border-color:#FDE68A;background:#FFFBEB;color:#92400E}.file-actions{margin-left:auto;display:flex;align-items:center;gap:6px;min-width:0;flex-wrap:wrap}.file-action{font:11px var(--sans);color:rgba(27,27,27,.50);text-decoration:none;border:1px solid var(--ws-border);border-radius:6px;padding:2px 7px;background:#fff;cursor:pointer}.file-action:hover{color:var(--ws-foreground);background:var(--ws-surface)}.file-icon-action{width:24px;height:24px;min-width:24px;padding:0;display:inline-flex;align-items:center;justify-content:center}.file-icon-action svg{width:13px;height:13px;display:block}select.file-action{height:23px;max-width:190px;padding:1px 24px 1px 7px}.empty{color:var(--ws-faint);padding:24px;font-size:12px}.error{color:var(--ws-error)}
|
|
1077
|
-
.monaco-host{flex:1;min-height:0}.code-fallback{flex:1;min-height:0;overflow:auto}.code-wrap{display:grid;grid-template-columns:auto minmax(0,1fr);align-items:start;min-height:100%;font:12px/1.55 var(--mono)}.line-nums{user-select:none;text-align:right;padding:14px 10px 14px 14px;color:rgba(27,27,27,.25);background:#FAF9F5;border-right:1px solid var(--ws-border-subtle);white-space:pre}.code-pre{margin:0;padding:14px;white-space:pre;overflow:auto;color:#1F2937;background:#fff;min-height:100%}.markdown-preview{flex:1;min-height:0;overflow:auto;max-width:920px;padding:24px 28px;color:rgba(27,27,27,.86);font-size:14px;line-height:1.65}.markdown-preview h1,.markdown-preview h2,.markdown-preview h3{line-height:1.2;margin:18px 0 8px}.markdown-preview p{margin:0 0 12px}.markdown-preview code,.message-content code{font-family:var(--mono);font-size:.92em;background:rgba(27,27,27,.06);border-radius:4px;padding:1px 4px}.markdown-preview pre,.message-content pre{overflow:auto;background:#0D1117;color:#E5E7EB;border-radius:7px;padding:12px}.image-stage{flex:1;min-height:0;display:flex;flex-direction:column;background:#F8F7F2}.image-toolbar{height:34px;flex:0 0 auto;display:flex;align-items:center;justify-content:flex-end;gap:6px;padding:0 10px;border-bottom:1px solid var(--ws-border-subtle);background:#fff}.image-preview{flex:1;min-height:0;display:flex;align-items:center;justify-content:center;padding:18px;overflow:auto}.image-preview img{max-width:100%;max-height:100%;object-fit:contain;border:1px solid var(--ws-border);background:#fff;box-shadow:0 10px 30px rgba(27,27,27,.10);transform-origin:center center}.frame-preview{flex:1;min-height:0;background:#F8F7F2}.frame-preview iframe{width:100%;height:100%;border:0;background:#fff}.table-preview{flex:1;min-height:0;padding:18px;overflow:auto}.table-preview .table-meta{margin:0 0 10px;color:rgba(27,27,27,.42);font:11px var(--mono)}.table-preview table{border-collapse:collapse;font-size:12px;background:#fff}.table-preview th,.table-preview td{border:1px solid var(--ws-border);padding:5px 7px;max-width:320px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.table-preview th{background:#F7F5EF;text-align:left;color:rgba(27,27,27,.65);position:sticky;top:0}.table-preview td.formula-cell{background:#F0F9FF;color:#075985}.diagram-preview{flex:1;min-height:0;padding:18px;display:grid;gap:14px;max-width:none;overflow:auto}.mermaid-card{border:1px solid var(--ws-border);border-radius:8px;background:#fff;overflow:hidden;display:flex;flex-direction:column}.mermaid-output{padding:18px;overflow:auto;min-height:220px;display:flex;align-items:center;justify-content:center}.mermaid-output svg{width:100%;max-width:100%;height:auto;display:block}.mermaid-source{margin:0;border-top:1px solid var(--ws-border-subtle);border-radius:0;background:#FAF9F5;color:rgba(27,27,27,.68);font:11px/1.45 var(--mono);max-height:220px;overflow:auto;padding:10px 12px;white-space:pre}.viewer-note{flex:1;min-height:0;overflow:auto;display:flex;align-items:center;justify-content:center;padding:24px;background:#FAF9F5}.viewer-note-card{width:min(520px,100%);max-height:100%;overflow:auto;border:1px solid var(--ws-border);border-radius:8px;background:#fff;padding:18px;box-shadow:0 10px 34px rgba(27,27,27,.05)}.viewer-note-title{font-size:14px;font-weight:750;color:rgba(27,27,27,.84)}.viewer-note-body{margin-top:8px;color:rgba(27,27,27,.56);font-size:12px;line-height:1.55}.viewer-note-actions{margin-top:14px;display:flex;gap:8px;flex-wrap:wrap}.binary-preview{flex:1;min-height:0;display:flex;align-items:center;justify-content:center;padding:24px;color:rgba(27,27,27,.50);text-align:center}
|
|
1163
|
+
.monaco-host{flex:1;min-height:0}.code-fallback{flex:1;min-height:0;overflow:auto}.code-wrap{display:grid;grid-template-columns:auto minmax(0,1fr);align-items:start;min-height:100%;font:12px/1.55 var(--mono)}.line-nums{user-select:none;text-align:right;padding:14px 10px 14px 14px;color:rgba(27,27,27,.25);background:#FAF9F5;border-right:1px solid var(--ws-border-subtle);white-space:pre}.code-pre{margin:0;padding:14px;white-space:pre;overflow:auto;color:#1F2937;background:#fff;min-height:100%}.markdown-preview{flex:1;min-height:0;overflow:auto;max-width:920px;padding:24px 28px;color:rgba(27,27,27,.86);font-size:14px;line-height:1.65}.markdown-preview h1,.markdown-preview h2,.markdown-preview h3{line-height:1.2;margin:18px 0 8px}.markdown-preview p{margin:0 0 12px}.markdown-preview code,.message-content code{font-family:var(--mono);font-size:.92em;background:rgba(27,27,27,.06);border-radius:4px;padding:1px 4px}.markdown-preview pre,.message-content pre{overflow:auto;background:#0D1117;color:#E5E7EB;border-radius:7px;padding:12px}.image-stage,.media-stage{flex:1;min-height:0;display:flex;flex-direction:column;background:#F8F7F2}.image-toolbar,.media-toolbar{height:34px;flex:0 0 auto;display:flex;align-items:center;justify-content:flex-end;gap:6px;padding:0 10px;border-bottom:1px solid var(--ws-border-subtle);background:#fff}.image-preview,.media-preview{flex:1;min-height:0;display:flex;align-items:center;justify-content:center;padding:18px;overflow:auto}.image-preview img{max-width:100%;max-height:100%;object-fit:contain;border:1px solid var(--ws-border);background:#fff;box-shadow:0 10px 30px rgba(27,27,27,.10);transform-origin:center center}.media-preview video{width:min(100%,1120px);max-height:100%;background:#0B0B0B;border-radius:8px;box-shadow:0 14px 42px rgba(27,27,27,.16)}.media-preview audio{width:min(100%,720px)}.frame-preview{flex:1;min-height:0;background:#F8F7F2}.frame-preview iframe{width:100%;height:100%;border:0;background:#fff}.table-preview{flex:1;min-height:0;padding:18px;overflow:auto}.table-preview .table-meta{margin:0 0 10px;color:rgba(27,27,27,.42);font:11px var(--mono)}.table-preview table{border-collapse:collapse;font-size:12px;background:#fff}.table-preview th,.table-preview td{border:1px solid var(--ws-border);padding:5px 7px;max-width:320px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.table-preview th{background:#F7F5EF;text-align:left;color:rgba(27,27,27,.65);position:sticky;top:0}.table-preview td.formula-cell{background:#F0F9FF;color:#075985}.diagram-preview{flex:1;min-height:0;padding:18px;display:grid;gap:14px;max-width:none;overflow:auto}.mermaid-card{border:1px solid var(--ws-border);border-radius:8px;background:#fff;overflow:hidden;display:flex;flex-direction:column}.mermaid-output{padding:18px;overflow:auto;min-height:220px;display:flex;align-items:center;justify-content:center}.mermaid-output svg{width:100%;max-width:100%;height:auto;display:block}.mermaid-source{margin:0;border-top:1px solid var(--ws-border-subtle);border-radius:0;background:#FAF9F5;color:rgba(27,27,27,.68);font:11px/1.45 var(--mono);max-height:220px;overflow:auto;padding:10px 12px;white-space:pre}.viewer-note{flex:1;min-height:0;overflow:auto;display:flex;align-items:center;justify-content:center;padding:24px;background:#FAF9F5}.viewer-note-card{width:min(520px,100%);max-height:100%;overflow:auto;border:1px solid var(--ws-border);border-radius:8px;background:#fff;padding:18px;box-shadow:0 10px 34px rgba(27,27,27,.05)}.viewer-note-title{font-size:14px;font-weight:750;color:rgba(27,27,27,.84)}.viewer-note-body{margin-top:8px;color:rgba(27,27,27,.56);font-size:12px;line-height:1.55}.viewer-note-actions{margin-top:14px;display:flex;gap:8px;flex-wrap:wrap}.binary-preview{flex:1;min-height:0;display:flex;align-items:center;justify-content:center;padding:24px;color:rgba(27,27,27,.50);text-align:center}
|
|
1078
1164
|
.chat-head{height:36px;border-bottom:1px solid var(--ws-border-subtle);display:flex;align-items:center;gap:8px;padding:0 12px;background:rgba(255,255,255,.55)}.chat-title{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.08em;color:rgba(27,27,27,.42)}.chat-subtitle{min-width:0;flex:1;font-size:12px;color:rgba(27,27,27,.42);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.agent-tabs{height:34px;display:flex;align-items:center;border-bottom:1px solid var(--ws-border-subtle);background:#F7F5EF;padding:0 8px;gap:3px}.agent-tab{height:26px;border:0;border-radius:var(--ws-radius-md);background:transparent;color:rgba(27,27,27,.48);font-size:13px;font-weight:500;padding:0 9px;cursor:pointer}.agent-tab:hover{background:var(--ws-hover);color:rgba(27,27,27,.76)}.agent-tab.active{background:#fff;color:var(--ws-foreground);font-weight:600;box-shadow:0 0 0 1px rgba(27,27,27,.06)}.chat-body{display:flex;flex-direction:column;flex:1;min-height:0}.chat-pane{display:none;flex-direction:column;flex:1;min-height:0}.chat-pane.active{display:flex}.plugins-menu-wrap{position:relative}.plugins-menu-wrap[hidden]{display:none}.plugins-dropdown{position:absolute;top:26px;left:0;z-index:60;min-width:210px;background:#fff;border:1px solid var(--ws-border);border-radius:var(--ws-radius-md);box-shadow:0 12px 34px rgba(27,27,27,.14);padding:4px;display:flex;flex-direction:column}.plugins-dropdown[hidden]{display:none}.plugins-dropdown button{border:0;background:transparent;text-align:left;font:12px var(--sans);color:rgba(27,27,27,.78);padding:6px 9px;border-radius:var(--ws-radius-sm);cursor:pointer;display:flex;align-items:center;justify-content:space-between;gap:10px}.plugins-dropdown button:hover{background:var(--ws-hover)}.plugins-dropdown .plugin-origin{color:var(--ws-faint);font:11px var(--sans)}.thread{flex:1;min-height:0;overflow:auto;padding:16px 14px 10px;background:rgba(255,255,255,.40)}.thread-inner{display:flex;flex-direction:column;gap:12px}.empty-chat{display:flex;height:100%;align-items:center;justify-content:center;text-align:center;color:rgba(27,27,27,.42);font-size:12px}.session-list{display:flex;flex-direction:column;gap:2px}.session-row{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:10px;align-items:center;width:100%;border:0;background:transparent;text-align:left;border-radius:var(--ws-radius-md);padding:8px;color:inherit;cursor:pointer}.session-row:hover{background:rgba(27,27,27,.035)}.session-row.selected{background:var(--ws-active);box-shadow:inset 2px 0 0 var(--ws-primary)}.session-row:focus-visible{outline:2px solid rgba(8,145,178,.35);outline-offset:1px}.session-main{min-width:0}.session-prompt{display:block;font-size:13px;font-weight:500;color:rgba(27,27,27,.84);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.session-meta{display:block;margin-top:3px;font:12px/1.4 var(--sans);color:rgba(27,27,27,.46);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.session-tools{margin-top:5px;display:flex;gap:4px;flex-wrap:wrap}.session-chip{border-radius:999px;border:1px solid rgba(27,27,27,.07);background:#F7F5EF;padding:1px 6px;font:11px var(--sans);color:rgba(27,27,27,.52)}.session-action{height:24px;display:inline-flex;align-items:center;justify-content:center;border:1px solid rgba(27,27,27,.10);border-radius:var(--ws-radius-md);background:#fff;color:rgba(27,27,27,.62);padding:0 8px;font-size:12px;font-weight:500}.session-row:hover .session-action{background:#F7F5EF;color:var(--ws-foreground)}.msg{display:flex}.msg.user{justify-content:flex-end}.msg.assistant{justify-content:flex-start}.bubble{max-width:86%;border-radius:16px;padding:10px 12px;font-size:13px;line-height:1.55;word-break:break-word}.user .bubble{background:#1B1B1B;color:#FFFDF7}.message-attachments{display:flex;flex-wrap:wrap;gap:5px;margin-top:8px}.message-attachment{max-width:100%;display:inline-flex;align-items:center;gap:5px;border:1px solid rgba(255,255,255,.20);border-radius:999px;background:rgba(255,255,255,.10);color:#FFFDF7;padding:2px 7px;cursor:pointer}.message-attachment span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:180px}.message-attachment small{font:11px var(--sans);opacity:.68}.message-attachment:hover{background:rgba(255,255,255,.17)}.assistant-stack{width:100%;max-width:960px;display:flex;flex-direction:column;gap:8px}.assistant-bubble{display:none;max-width:94%;border-radius:16px;background:#F7F5EF;padding:11px 13px;color:rgba(27,27,27,.86);font-size:13px;line-height:1.6}.assistant-bubble:not(:empty){display:block}.message-content p{margin:0 0 10px}.message-content p:last-child{margin-bottom:0}.message-content ul{margin:0 0 10px 18px;padding:0}.message-content li{margin:2px 0}
|
|
1079
1165
|
.activity-card{display:none;overflow:hidden;border:1px solid var(--ws-border);border-radius:8px;background:#FFFDF7;font-size:12px}.activity-card.active{display:block}.activity-head{height:28px;width:100%;border:0;background:transparent;display:flex;align-items:center;gap:8px;padding:0 9px;cursor:pointer;color:rgba(27,27,27,.68)}.activity-head:hover{background:#F7F5EF}.activity-label{min-width:0;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:left;font-size:11px;font-weight:650}.activity-rollup{font-size:10px;color:rgba(27,27,27,.38)}.activity-rows{border-top:1px solid rgba(27,27,27,.05);padding:7px 9px;max-height:96px;overflow:auto;display:flex;flex-direction:column;gap:5px}.activity-card.expanded .activity-rows{max-height:360px}.activity-row{display:flex;align-items:flex-start;gap:7px;color:rgba(27,27,27,.65);font-size:11px;line-height:1.35}.activity-row .status-dot{width:8px;height:8px;border-radius:999px;background:rgba(27,27,27,.18);margin-top:4px;flex:0 0 auto}.activity-row.running .status-dot{background:#0891B2}.activity-row.done .status-dot{background:#059669}.activity-row.error .status-dot{background:#DC2626}.activity-row.approval .status-dot{background:#D97706}.activity-row.thinking{font-style:italic;color:rgba(27,27,27,.48)}.activity-main{display:block;min-width:0;flex:1}.activity-message{display:block}.activity-row pre{display:none;margin:4px 0 0;max-height:120px;overflow:auto;border-radius:6px;background:rgba(27,27,27,.04);padding:6px;font:10px/1.4 var(--mono);color:rgba(27,27,27,.62);white-space:pre-wrap}.activity-card.expanded .activity-row pre{display:block}.activity-approval{margin-top:7px;border:1px solid rgba(217,119,6,.20);border-radius:7px;background:#FFFBEB;overflow:hidden;transition:opacity .85s ease,transform .85s ease,max-height .85s ease,margin .85s ease}.activity-approval.fading{opacity:0;transform:translateY(-4px);max-height:0;margin:0;pointer-events:none}.activity-approval-head{display:flex;align-items:center;justify-content:space-between;gap:8px;border-bottom:1px solid rgba(217,119,6,.12);padding:6px 8px}.activity-approval-title{font-size:11px;font-weight:800;color:#92400E}.activity-approval-meta{font:10px var(--mono);color:rgba(146,64,14,.58);white-space:nowrap}.activity-approval-subject{margin:7px 8px 0;border:1px solid rgba(217,119,6,.14);border-radius:5px;background:#fff;padding:7px;font:10px/1.45 var(--mono);color:rgba(27,27,27,.72);white-space:pre-wrap;overflow-wrap:anywhere;max-height:130px;overflow:auto}.activity-approval-reason{padding:6px 8px 8px;color:rgba(27,27,27,.56);font-size:11px;line-height:1.45}.activity-approval-actions{display:flex;justify-content:flex-end;gap:6px;flex-wrap:wrap;border-top:1px solid rgba(217,119,6,.12);background:rgba(255,255,255,.58);padding:7px 8px}.activity-result{margin-top:6px}.trace{display:block;flex:1;min-height:0;overflow:auto;background:#fff;padding:8px}.trace-row{font:11px/1.45 var(--mono);border-bottom:1px solid rgba(27,27,27,.05);padding:7px 4px;color:rgba(27,27,27,.52);white-space:pre-wrap;overflow-wrap:anywhere}.trace-row b{color:var(--ws-primary)}
|
|
1080
1166
|
.notebook-preview{flex:1;min-height:0;overflow:auto;background:#F7F5EF;padding:14px 14px 40px;display:flex;flex-direction:column;gap:10px}.notebook-meta{font:11px var(--mono);color:rgba(27,27,27,.42);padding:0 6px}.notebook-cell{border:1px solid var(--ws-border-subtle);border-radius:8px;background:#fff;overflow:hidden;box-shadow:0 1px 0 rgba(27,27,27,.02)}.notebook-cell.code{display:grid;grid-template-columns:64px minmax(0,1fr);padding:0;align-items:stretch}.notebook-cell.markdown{padding:16px 20px}.notebook-cell.raw{background:#F7F5EF;padding:12px 16px}.notebook-cell.raw .notebook-cell-body{padding:0}.notebook-cell.raw pre{margin:0;white-space:pre-wrap;font:12px/1.5 var(--mono);color:rgba(27,27,27,.72)}.notebook-cell-body{padding:0}.notebook-markdown{font-size:14px;line-height:1.65;color:rgba(27,27,27,.86)}.notebook-markdown h1,.notebook-markdown h2,.notebook-markdown h3{line-height:1.2;margin:14px 0 8px;color:rgba(27,27,27,.92)}.notebook-markdown h1{font-size:22px;border-bottom:1px solid var(--ws-border-subtle);padding-bottom:4px}.notebook-markdown p{margin:0 0 10px}.notebook-markdown code{font-family:var(--mono);font-size:.92em;background:rgba(27,27,27,.06);border-radius:4px;padding:1px 4px}.notebook-markdown pre{overflow:auto;background:#0D1117;color:#E5E7EB;border-radius:6px;padding:10px}.notebook-prompt{padding:12px 10px 12px 10px;font:11px/1.4 var(--mono);text-align:right;user-select:none;color:#4B6BFB;background:transparent;border-right:1px solid var(--ws-border-subtle);white-space:nowrap;display:flex;align-items:flex-start;justify-content:flex-end}.notebook-prompt.out{color:#A21818}.notebook-prompt.empty{color:transparent;pointer-events:none}.notebook-prompt.in::before{content:"In "}.notebook-prompt.out::before{content:"Out "}.notebook-cell-monaco{background:#FAFBFC;min-height:52px;position:relative;overflow:hidden;padding:6px 0}.notebook-cell-monaco .monaco-editor,.notebook-cell-monaco .monaco-editor .overflow-guard,.notebook-cell-monaco .monaco-editor-background,.notebook-cell-monaco .margin{background:#FAFBFC!important}.notebook-code-fallback{margin:0;padding:8px 12px;background:transparent;color:#1F2937;font:12px/1.5 var(--mono);white-space:pre;overflow:auto}.notebook-cell-outputs{grid-column:1 / -1;display:grid;grid-template-columns:64px minmax(0,1fr);background:#fff;border-top:1px solid var(--ws-border-subtle)}.notebook-output-item{display:contents}.notebook-output-item+.notebook-output-item>.notebook-prompt,.notebook-output-item+.notebook-output-item>.notebook-output-body{border-top:1px solid var(--ws-border-subtle)}.notebook-output-body{padding:8px 12px;overflow:auto;min-width:0;background:#fff}.notebook-stream{margin:0;font:12px/1.5 var(--mono);color:#1F2937;white-space:pre-wrap;word-break:break-word;background:transparent;padding:0}.notebook-stream.stderr{color:#7F1D1D;background:#FEF2F2;padding:6px 8px;border-radius:5px}.notebook-error{margin:0;font:12px/1.5 var(--mono);color:#7F1D1D;background:#FEF2F2;padding:8px 10px;border-radius:5px;white-space:pre-wrap;word-break:break-word;overflow:auto}.notebook-plain{margin:0;font:12px/1.5 var(--mono);color:#1F2937;white-space:pre-wrap;word-break:break-word;background:transparent;padding:0}.notebook-json{margin:0;font:12px/1.5 var(--mono);color:#0F172A;background:#F8FAFC;padding:8px 10px;border-radius:5px;white-space:pre;overflow:auto}.notebook-output-body img{max-width:100%;height:auto;display:block;background:#fff;border-radius:4px}.notebook-output-body iframe{width:100%;min-height:220px;border:1px solid var(--ws-border-subtle);border-radius:5px;background:#fff}.notebook-latex{font-size:14px;color:rgba(27,27,27,.90);padding:6px 0;overflow:auto}.katex-display{margin:6px 0!important}.viewer.maximized{position:fixed;inset:12px;z-index:70;border:1px solid rgba(27,27,27,.12);border-radius:8px;box-shadow:0 24px 80px rgba(27,27,27,.28);background:#fff}.viewer.maximized .diagram-preview,.viewer.maximized .markdown-preview{max-width:none}.viewer.maximized .mermaid-output{min-height:calc(100vh - 220px)}.composer{border-top:1px solid var(--ws-border-subtle);background:#FFFDF7;padding:10px 12px}.composer-box{border:1px solid var(--ws-border);border-radius:10px;background:#fff;overflow:hidden}.upload-tray{display:flex;gap:6px;flex-wrap:wrap;padding:8px 9px 0}.upload-tray[hidden]{display:none}.upload-chip{height:24px;display:inline-flex;align-items:center;gap:6px;max-width:100%;border:1px solid rgba(8,145,178,.18);border-radius:999px;background:#F0F9FF;color:#075985;padding:0 4px 0 8px;font-size:11px}.upload-chip-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:210px}.upload-chip-meta{font:10px var(--mono);color:rgba(7,89,133,.58)}.upload-chip button{width:18px;height:18px;border:0;border-radius:999px;background:transparent;color:rgba(7,89,133,.52);cursor:pointer;padding:0}.upload-chip button:hover{background:rgba(8,145,178,.12);color:#075985}textarea{display:block;width:100%;min-height:84px;max-height:240px;resize:vertical;border:0;padding:10px 11px;background:#fff;color:var(--ws-foreground);outline:none}.composer-actions{height:34px;border-top:1px solid var(--ws-border-subtle);display:flex;align-items:center;justify-content:space-between;padding:0 8px}.composer-left,.composer-right{display:flex;align-items:center;gap:6px;min-width:0}.composer-right{margin-left:auto}.status{font-size:11px;color:var(--ws-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding-right:8px}.stop-button{height:24px;border:1px solid rgba(185,28,28,.20);border-radius:6px;background:#FEF2F2;color:#B91C1C;font-size:11px;font-weight:750;padding:0 8px;display:inline-flex;align-items:center;gap:5px;cursor:pointer}.stop-button svg{width:12px;height:12px}.stop-button:hover{background:#FEE2E2;border-color:rgba(185,28,28,.30)}.stop-button:disabled{opacity:.55;cursor:not-allowed}.stop-button[hidden]{display:none}button.primary{height:24px;border:0;border-radius:6px;background:var(--ws-foreground);color:#fff;font-size:11px;font-weight:750;padding:0 10px;cursor:pointer}button.primary:hover{background:#2B2B2B}button.primary:disabled{opacity:.45;cursor:not-allowed}
|
|
@@ -1137,6 +1223,17 @@ main.hide-files{grid-template-columns:0 0 minmax(360px,1fr) 6px var(--right-w)}m
|
|
|
1137
1223
|
<main id="layout">
|
|
1138
1224
|
<section class="panel files" id="filesPanel">
|
|
1139
1225
|
<div class="section-head"><span>Files</span><span class="section-actions"><button class="section-button" id="refreshFiles" type="button" title="Refresh">↻</button><span id="fileCount"></span></span></div>
|
|
1226
|
+
<div class="workspace-tools">
|
|
1227
|
+
<input class="file-search" id="fileSearch" type="search" placeholder="Search workspace" autocomplete="off">
|
|
1228
|
+
<div class="file-filters" id="fileFilters" aria-label="File type filters">
|
|
1229
|
+
<button class="filter-chip active" type="button" data-file-filter="all">All</button>
|
|
1230
|
+
<button class="filter-chip" type="button" data-file-filter="code">Code</button>
|
|
1231
|
+
<button class="filter-chip" type="button" data-file-filter="docs">Docs</button>
|
|
1232
|
+
<button class="filter-chip" type="button" data-file-filter="data">Data</button>
|
|
1233
|
+
<button class="filter-chip" type="button" data-file-filter="media">Media</button>
|
|
1234
|
+
<button class="filter-chip" type="button" data-file-filter="web">Web</button>
|
|
1235
|
+
</div>
|
|
1236
|
+
</div>
|
|
1140
1237
|
<div class="file-list" id="fileList"></div>
|
|
1141
1238
|
</section>
|
|
1142
1239
|
<div class="resizer left-resizer" data-resize="left"></div>
|
|
@@ -1217,6 +1314,10 @@ const headers = { 'X-Bahulam-Local-Token': token };
|
|
|
1217
1314
|
const dirCache = new Map();
|
|
1218
1315
|
const expandedDirs = new Set(['.']);
|
|
1219
1316
|
const loadingDirs = new Set();
|
|
1317
|
+
let workspaceQuery = '';
|
|
1318
|
+
let workspaceFilter = 'all';
|
|
1319
|
+
let workspaceSearchTimer = null;
|
|
1320
|
+
let workspaceSearchSeq = 0;
|
|
1220
1321
|
const openTabs = [];
|
|
1221
1322
|
const fileCache = new Map();
|
|
1222
1323
|
let activePath = null;
|
|
@@ -1282,7 +1383,10 @@ function attachmentHint(file){
|
|
|
1282
1383
|
const mime=String(file?.mime_type||'').toLowerCase();
|
|
1283
1384
|
const ext=extname(file?.path||file?.name||'');
|
|
1284
1385
|
if(kind==='image'||mime.startsWith('image/'))return 'vision';
|
|
1386
|
+
if(kind==='video'||mime.startsWith('video/'))return 'video';
|
|
1387
|
+
if(kind==='audio'||mime.startsWith('audio/'))return 'audio';
|
|
1285
1388
|
if(kind==='spreadsheet'||kind==='table'||['csv','tsv','xlsx','xls','ods'].includes(ext))return 'table';
|
|
1389
|
+
if(kind==='web')return 'web';
|
|
1286
1390
|
if(['pdf','markdown','text','code','config','notebook'].includes(kind)||['txt','md','mdx','pdf','json','yaml','yml','toml','html','xml','ipynb','log','rst','sql','sh'].includes(ext))return 'read';
|
|
1287
1391
|
return 'file';
|
|
1288
1392
|
}
|
|
@@ -1368,8 +1472,11 @@ function fileKind(data){
|
|
|
1368
1472
|
const file=data.file||{};
|
|
1369
1473
|
if(file.viewer)return file.viewer;
|
|
1370
1474
|
const ext=extname(data.path);
|
|
1371
|
-
if(['png','jpg','jpeg','gif','webp','svg','bmp','ico'].includes(ext))return 'image';
|
|
1475
|
+
if(['png','jpg','jpeg','gif','webp','svg','bmp','ico','avif'].includes(ext))return 'image';
|
|
1476
|
+
if(['mp4','webm','mov','m4v','ogv'].includes(ext))return 'video';
|
|
1477
|
+
if(['mp3','m4a','wav','ogg','flac','aac'].includes(ext))return 'audio';
|
|
1372
1478
|
if(ext==='pdf')return 'pdf';
|
|
1479
|
+
if(['html','htm'].includes(ext))return 'web';
|
|
1373
1480
|
if(['md','mdx'].includes(ext))return 'markdown';
|
|
1374
1481
|
if(['mmd','mermaid'].includes(ext))return 'mermaid';
|
|
1375
1482
|
if(['drawio','dio'].includes(ext))return 'drawio';
|
|
@@ -1502,7 +1609,7 @@ function bindHeaderActions(path){
|
|
|
1502
1609
|
}
|
|
1503
1610
|
function askAgentForFile(path,kind){
|
|
1504
1611
|
const prompt=document.getElementById('prompt');
|
|
1505
|
-
const label=kind==='image'?'Analyze this image':kind==='pdf'?'Summarize this PDF':kind==='spreadsheet'?'Inspect this spreadsheet':kind==='document'?'Inspect this document':kind==='presentation'?'Inspect this presentation':'Inspect this file';
|
|
1612
|
+
const label=kind==='image'?'Analyze this image':kind==='video'?'Inspect this video':kind==='audio'?'Inspect this audio':kind==='web'?'Review this web page':kind==='pdf'?'Summarize this PDF':kind==='spreadsheet'?'Inspect this spreadsheet':kind==='document'?'Inspect this document':kind==='presentation'?'Inspect this presentation':'Inspect this file';
|
|
1506
1613
|
prompt.value=label+' at '+path+'. Explain what it contains and call the right local tools if needed.';
|
|
1507
1614
|
prompt.focus();
|
|
1508
1615
|
}
|
|
@@ -1695,11 +1802,75 @@ function renderTabs(){
|
|
|
1695
1802
|
function renderExplorer(){
|
|
1696
1803
|
const root=dirCache.get('.');
|
|
1697
1804
|
const list=document.getElementById('fileList');
|
|
1805
|
+
if(workspaceSearchActive()){scheduleWorkspaceSearch(0);return;}
|
|
1698
1806
|
if(!root){list.innerHTML='<div class="loading-row">Loading...</div>';return;}
|
|
1699
1807
|
list.innerHTML=renderEntries(root.entries||[],0);
|
|
1700
1808
|
list.querySelectorAll('[data-dir]').forEach(btn=>btn.addEventListener('click',async()=>{const p=btn.dataset.dir;if(expandedDirs.has(p)&&p!=='.')expandedDirs.delete(p);else{expandedDirs.add(p);if(!dirCache.has(p))await loadDir(p);}renderExplorer();}));
|
|
1701
1809
|
list.querySelectorAll('[data-file]').forEach(btn=>btn.addEventListener('click',()=>openFile(btn.dataset.file)));
|
|
1702
1810
|
}
|
|
1811
|
+
function workspaceSearchActive(){
|
|
1812
|
+
return Boolean(workspaceQuery.trim() || workspaceFilter !== 'all');
|
|
1813
|
+
}
|
|
1814
|
+
function filterKinds(filter){
|
|
1815
|
+
switch(filter){
|
|
1816
|
+
case 'code': return ['code','config'];
|
|
1817
|
+
case 'docs': return ['markdown','pdf','document','presentation','notebook'];
|
|
1818
|
+
case 'data': return ['table','spreadsheet'];
|
|
1819
|
+
case 'media': return ['image','video','audio'];
|
|
1820
|
+
case 'web': return ['web','mermaid','drawio'];
|
|
1821
|
+
default: return [];
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
function setWorkspaceFilter(filter){
|
|
1825
|
+
workspaceFilter=filter||'all';
|
|
1826
|
+
document.querySelectorAll('[data-file-filter]').forEach(btn=>btn.classList.toggle('active',btn.dataset.fileFilter===workspaceFilter));
|
|
1827
|
+
renderExplorer();
|
|
1828
|
+
}
|
|
1829
|
+
function scheduleWorkspaceSearch(delay=160){
|
|
1830
|
+
if(workspaceSearchTimer)clearTimeout(workspaceSearchTimer);
|
|
1831
|
+
workspaceSearchTimer=setTimeout(loadWorkspaceSearch,delay);
|
|
1832
|
+
}
|
|
1833
|
+
async function loadWorkspaceSearch(){
|
|
1834
|
+
if(!workspaceSearchActive())return;
|
|
1835
|
+
const list=document.getElementById('fileList');
|
|
1836
|
+
const seq=++workspaceSearchSeq;
|
|
1837
|
+
const kinds=filterKinds(workspaceFilter);
|
|
1838
|
+
list.innerHTML='<div class="loading-row">Searching...</div>';
|
|
1839
|
+
try{
|
|
1840
|
+
const url='/api/workspace/search?q='+encodeURIComponent(workspaceQuery.trim())+(kinds.length?'&kind='+encodeURIComponent(kinds.join(',')):'');
|
|
1841
|
+
const result=await api(url);
|
|
1842
|
+
if(seq!==workspaceSearchSeq)return;
|
|
1843
|
+
document.getElementById('fileCount').textContent=String((result.results||[]).length);
|
|
1844
|
+
renderSearchResults(result);
|
|
1845
|
+
}catch(err){
|
|
1846
|
+
if(seq!==workspaceSearchSeq)return;
|
|
1847
|
+
list.innerHTML='<div class="search-results error">'+esc(err.message||'Search failed')+'</div>';
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
function renderSearchResults(result){
|
|
1851
|
+
const list=document.getElementById('fileList');
|
|
1852
|
+
const rows=Array.isArray(result.results)?result.results:[];
|
|
1853
|
+
const note='<div class="search-results">'+esc(rows.length)+' result'+(rows.length===1?'':'s')+(result.truncated?' · truncated':'')+'</div>';
|
|
1854
|
+
if(!rows.length){list.innerHTML=note+'<div class="empty">No matching files.</div>';return;}
|
|
1855
|
+
list.innerHTML=note+rows.map(entry=>{
|
|
1856
|
+
const attrs=entry.type==='directory'?'data-dir="'+esc(entry.path)+'"':'data-file="'+esc(entry.path)+'"';
|
|
1857
|
+
return '<button class="row '+(entry.path===activePath?'selected':'')+'" '+attrs+' title="'+esc(entry.path)+'"><span class="chev"></span><span class="icon">'+entryIcon(entry)+'</span><span class="name">'+esc(entry.path)+'</span><span class="sub">'+esc(entry.label||entry.kind||'')+'</span></button>';
|
|
1858
|
+
}).join('');
|
|
1859
|
+
list.querySelectorAll('[data-dir]').forEach(btn=>btn.addEventListener('click',async()=>{const dir=btn.dataset.dir;expandedDirs.add(dir);workspaceQuery='';document.getElementById('fileSearch').value='';setWorkspaceFilter('all');if(!dirCache.has(dir))await loadDir(dir);else renderExplorer();}));
|
|
1860
|
+
list.querySelectorAll('[data-file]').forEach(btn=>btn.addEventListener('click',()=>openFile(btn.dataset.file)));
|
|
1861
|
+
}
|
|
1862
|
+
function entryIcon(entry){
|
|
1863
|
+
const kind=String(entry.kind||entry.viewer||'').toLowerCase();
|
|
1864
|
+
if(entry.type==='directory'||kind==='directory')return '▦';
|
|
1865
|
+
if(kind==='image')return '◉';
|
|
1866
|
+
if(kind==='video')return '▶';
|
|
1867
|
+
if(kind==='audio')return '♪';
|
|
1868
|
+
if(kind==='web')return '⌘';
|
|
1869
|
+
if(['spreadsheet','table'].includes(kind))return '▤';
|
|
1870
|
+
if(['markdown','pdf','document','presentation','notebook'].includes(kind))return '▣';
|
|
1871
|
+
if(['code','config','mermaid','drawio'].includes(kind))return '◇';
|
|
1872
|
+
return '·';
|
|
1873
|
+
}
|
|
1703
1874
|
function renderEntries(entries,depth){
|
|
1704
1875
|
return entries.map(entry=>{
|
|
1705
1876
|
const pad=8+depth*13;
|
|
@@ -1709,7 +1880,7 @@ function renderEntries(entries,depth){
|
|
|
1709
1880
|
const rows=open?(child?renderEntries(child.entries||[],depth+1):'<div class="loading-row" style="padding-left:'+(pad+24)+'px">Loading...</div>'):'';
|
|
1710
1881
|
return '<button class="row" style="padding-left:'+pad+'px" data-dir="'+esc(entry.path)+'"><span class="chev">'+(open?'⌄':'›')+'</span><span class="icon">▦</span><span class="name">'+esc(entry.name)+'</span></button>'+rows;
|
|
1711
1882
|
}
|
|
1712
|
-
return '<button class="row '+(entry.path===activePath?'selected':'')+'" style="padding-left:'+pad+'px" data-file="'+esc(entry.path)+'"><span class="chev"></span><span class="icon"
|
|
1883
|
+
return '<button class="row '+(entry.path===activePath?'selected':'')+'" style="padding-left:'+pad+'px" data-file="'+esc(entry.path)+'"><span class="chev"></span><span class="icon">'+entryIcon(entry)+'</span><span class="name">'+esc(entry.name)+'</span><span class="sub">'+(entry.size!=null?formatBytes(entry.size):'')+'</span></button>';
|
|
1713
1884
|
}).join('');
|
|
1714
1885
|
}
|
|
1715
1886
|
function renderEmptyViewer(){
|
|
@@ -1720,6 +1891,8 @@ function renderFile(data){
|
|
|
1720
1891
|
const text=data.preview&&data.preview.content!=null?sourceTextForRender(data):null;
|
|
1721
1892
|
const kind=fileKind(data);
|
|
1722
1893
|
if(kind==='image'){renderImage(data);return;}
|
|
1894
|
+
if(kind==='video'||kind==='audio'){renderMedia(data,kind);return;}
|
|
1895
|
+
if(kind==='web'&&text!=null){renderWebFile(data,text);return;}
|
|
1723
1896
|
if(kind==='pdf'){renderPdf(data);return;}
|
|
1724
1897
|
if(kind==='markdown'&&text!=null){renderMarkdownFile(data,text);return;}
|
|
1725
1898
|
if(kind==='mermaid'&&text!=null){renderMermaidFile(data,text);return;}
|
|
@@ -1929,11 +2102,25 @@ function renderImage(data){
|
|
|
1929
2102
|
if(scale)scale.textContent=Math.round(activeImageScale*100)+'%';
|
|
1930
2103
|
}));
|
|
1931
2104
|
}
|
|
2105
|
+
function renderMedia(data,kind){
|
|
2106
|
+
const header=fileHeader(data,{extraActions:'<span class="file-chip">'+esc(kind)+'</span>'});
|
|
2107
|
+
const source=rawUrl(data.path);
|
|
2108
|
+
const body=kind==='video'
|
|
2109
|
+
? '<video controls playsinline preload="metadata" src="'+source+'"></video>'
|
|
2110
|
+
: '<audio controls preload="metadata" src="'+source+'"></audio>';
|
|
2111
|
+
renderViewer(header+'<div class="media-stage" data-preview-content><div class="media-toolbar"><span class="file-chip">'+esc(formatBytes(data.file?.size))+'</span></div><div class="media-preview">'+body+'</div></div>');
|
|
2112
|
+
bindHeaderActions(data.path);
|
|
2113
|
+
}
|
|
1932
2114
|
function renderPdf(data){
|
|
1933
2115
|
const header=fileHeader(data);
|
|
1934
2116
|
renderViewer(header+'<div class="frame-preview" data-preview-content><iframe src="'+rawUrl(data.path)+'" title="'+esc(data.path)+'"></iframe></div>');
|
|
1935
2117
|
bindHeaderActions(data.path);
|
|
1936
2118
|
}
|
|
2119
|
+
function renderWebFile(data,text){
|
|
2120
|
+
const header=fileHeader(data,{language:'html',truncated:Boolean(data.preview&&data.preview.truncated),save:canEditFile(data),editSource:canEditFile(data),extraActions:'<span class="file-chip">web preview</span>'});
|
|
2121
|
+
renderViewer(header+'<div class="frame-preview" data-preview-content><iframe sandbox="allow-scripts allow-forms allow-modals" srcdoc="'+esc(text)+'" title="'+esc(data.path)+'"></iframe></div>');
|
|
2122
|
+
bindHeaderActions(data.path);
|
|
2123
|
+
}
|
|
1937
2124
|
function renderMarkdownFile(data,text){
|
|
1938
2125
|
const header=fileHeader(data,{language:'markdown',truncated:Boolean(data.preview&&data.preview.truncated),save:canEditFile(data),editSource:canEditFile(data)});
|
|
1939
2126
|
renderViewer(header+'<div class="markdown-preview" data-preview-content>'+markdown(text)+'</div>');
|
|
@@ -2942,7 +3129,9 @@ document.getElementById('sessionModal').addEventListener('click',(e)=>{if(e.targ
|
|
|
2942
3129
|
document.getElementById('approvalDenyTop').addEventListener('click',()=>submitApproval('reject'));
|
|
2943
3130
|
document.getElementById('approvalAuto').addEventListener('change',e=>setApprovalAutoMode(e.target.checked));
|
|
2944
3131
|
document.addEventListener('keydown',(e)=>{if(e.key==='Escape'&&pendingApproval){submitApproval('reject');return;}if(e.key==='Escape'&&sessionMenuOpen){setSessionMenuOpen(false);return;}if(e.key==='Escape'&&previewMaximized)setPreviewMaximized(false);});
|
|
2945
|
-
document.getElementById('
|
|
3132
|
+
document.getElementById('fileSearch').addEventListener('input',e=>{workspaceQuery=e.target.value||'';renderExplorer();});
|
|
3133
|
+
document.querySelectorAll('[data-file-filter]').forEach(btn=>btn.addEventListener('click',()=>setWorkspaceFilter(btn.dataset.fileFilter)));
|
|
3134
|
+
document.getElementById('refreshFiles').addEventListener('click',()=>{dirCache.clear();if(workspaceSearchActive())scheduleWorkspaceSearch(0);else loadDir('.');});
|
|
2946
3135
|
document.getElementById('toggleExplorer').addEventListener('click',()=>{let prefs={explorerVisible:true};try{prefs={...prefs,...JSON.parse(localStorage.getItem(layoutKey)||'{}')};}catch{}saveLayoutPatch({explorerVisible:!prefs.explorerVisible});});
|
|
2947
3136
|
document.getElementById('toggleAgent').addEventListener('click',()=>{let prefs={agentVisible:true};try{prefs={...prefs,...JSON.parse(localStorage.getItem(layoutKey)||'{}')};}catch{}saveLayoutPatch({agentVisible:!prefs.agentVisible});});
|
|
2948
3137
|
applyLayout();
|
package/src/plugins/manifest.mjs
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import fs from 'fs';
|
|
8
8
|
import path from 'path';
|
|
9
9
|
import { load as yamlLoad } from 'js-yaml';
|
|
10
|
+
import { normalizeComposes } from './pi-compose.mjs';
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Parse a YAML text string into an object using js-yaml.
|
|
@@ -185,6 +186,7 @@ export function normalizeManifest(raw, source = '') {
|
|
|
185
186
|
// Inline wins on name collision so authors can override a portable
|
|
186
187
|
// config for the local plugin without editing mcp.json.
|
|
187
188
|
const mcpServers = _readMcpServers(spec.mcpServers, source);
|
|
189
|
+
const composes = normalizeComposes(spec.composes);
|
|
188
190
|
|
|
189
191
|
return {
|
|
190
192
|
apiVersion,
|
|
@@ -201,6 +203,7 @@ export function normalizeManifest(raw, source = '') {
|
|
|
201
203
|
agents,
|
|
202
204
|
workspace,
|
|
203
205
|
mcpServers,
|
|
206
|
+
composes,
|
|
204
207
|
},
|
|
205
208
|
source,
|
|
206
209
|
_dir: source ? path.dirname(source) : '',
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared npm-install helper for pack and ingredient installs.
|
|
3
|
+
*
|
|
4
|
+
* Two responsibilities:
|
|
5
|
+
* 1. Materialize node_modules/ from package.json::dependencies
|
|
6
|
+
* (co-located with the package/pack, no shared/global install).
|
|
7
|
+
* 2. Gate execution of postinstall scripts on a verified-package
|
|
8
|
+
* allowlist. Unverified packages run `npm install --ignore-scripts`
|
|
9
|
+
* so a malicious postinstall can't execute on `bahulam pull`.
|
|
10
|
+
*
|
|
11
|
+
* Pi peer-dep quirk: pi packages often declare peerDependencies with `*`
|
|
12
|
+
* ranges, which crashes npm arborist. We migrate peers → dependencies
|
|
13
|
+
* before install. Native packs (hand-authored) don't need that step;
|
|
14
|
+
* their package.json is already ordinary.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import * as fs from 'node:fs';
|
|
18
|
+
import * as path from 'node:path';
|
|
19
|
+
import * as os from 'node:os';
|
|
20
|
+
import { spawn } from 'node:child_process';
|
|
21
|
+
|
|
22
|
+
const DIM = '\x1b[2m';
|
|
23
|
+
const YELLOW = '\x1b[33m';
|
|
24
|
+
const RESET = '\x1b[0m';
|
|
25
|
+
|
|
26
|
+
// Verified list: packages we've reviewed and trust to run postinstall.
|
|
27
|
+
// v1 ships an inline dev-time list that mirrors what we're actively
|
|
28
|
+
// using. Long term this migrates to `~/.bahulam/verified-pi-packages.json`
|
|
29
|
+
// synced from awesome-bahulam-plugins (§13.6.1c of PRD-102).
|
|
30
|
+
//
|
|
31
|
+
// The dev-time list intentionally covers only pi packages we've actually
|
|
32
|
+
// pulled and confirmed. Every other package installs with --ignore-scripts
|
|
33
|
+
// until reviewed.
|
|
34
|
+
const DEV_VERIFIED_PI_PACKAGES = new Set([
|
|
35
|
+
'pi-web-access',
|
|
36
|
+
'pi-redmine',
|
|
37
|
+
'pi-goal-x',
|
|
38
|
+
'@speclip/pi-media',
|
|
39
|
+
]);
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Determine whether a package is verified (postinstall scripts allowed).
|
|
43
|
+
* Reads ~/.bahulam/verified-pi-packages.json if present; falls back to
|
|
44
|
+
* the built-in dev list.
|
|
45
|
+
*/
|
|
46
|
+
export function isPackageVerified(packageName, { bahulamHomeDir = null } = {}) {
|
|
47
|
+
if (!packageName) return false;
|
|
48
|
+
if (DEV_VERIFIED_PI_PACKAGES.has(packageName)) return true;
|
|
49
|
+
const home = bahulamHomeDir || process.env.BAHULAM_HOME || path.join(os.homedir(), '.bahulam');
|
|
50
|
+
const registryPath = path.join(home, 'verified-pi-packages.json');
|
|
51
|
+
if (fs.existsSync(registryPath)) {
|
|
52
|
+
try {
|
|
53
|
+
const data = JSON.parse(fs.readFileSync(registryPath, 'utf-8'));
|
|
54
|
+
if (data && typeof data === 'object' && data[packageName]) return true;
|
|
55
|
+
} catch { /* fall through */ }
|
|
56
|
+
}
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function runNpm(args, opts) {
|
|
61
|
+
return new Promise((resolve, reject) => {
|
|
62
|
+
const child = spawn('npm', args, { stdio: ['ignore', 'pipe', 'pipe'], ...opts });
|
|
63
|
+
let stderr = '';
|
|
64
|
+
child.stderr.on('data', d => stderr += d);
|
|
65
|
+
child.on('close', code => {
|
|
66
|
+
if (code === 0) resolve();
|
|
67
|
+
else reject(new Error(`npm ${args.join(' ')} exited ${code}: ${stderr.slice(0, 400)}`));
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Migrate peerDependencies → dependencies (pi packages only) to sidestep
|
|
74
|
+
* the npm arborist crash on `*` ranges. Idempotent.
|
|
75
|
+
*/
|
|
76
|
+
function migratePeersForPi(pkgPath) {
|
|
77
|
+
let pkg = {};
|
|
78
|
+
try { pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8')); } catch { return {}; }
|
|
79
|
+
const peers = pkg.peerDependencies || {};
|
|
80
|
+
if (!Object.keys(peers).length) return pkg;
|
|
81
|
+
const merged = { ...(pkg.dependencies || {}) };
|
|
82
|
+
for (const [name, range] of Object.entries(peers)) {
|
|
83
|
+
if (!merged[name]) merged[name] = range === '*' ? 'latest' : range;
|
|
84
|
+
}
|
|
85
|
+
const rewritten = { ...pkg, dependencies: merged };
|
|
86
|
+
delete rewritten.peerDependencies;
|
|
87
|
+
fs.writeFileSync(pkgPath, JSON.stringify(rewritten, null, 2));
|
|
88
|
+
return rewritten;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Install the package's dependencies into a co-located node_modules.
|
|
93
|
+
* If the package.json has no deps, no-op (returns { installed: 0 }).
|
|
94
|
+
*
|
|
95
|
+
* @param {Object} opts
|
|
96
|
+
* @param {string} opts.dir Package/pack root directory
|
|
97
|
+
* @param {string} [opts.packageName] For verified-list lookup + logs
|
|
98
|
+
* @param {'pi'|'pack'} [opts.kind] Which peer-migration behavior to use
|
|
99
|
+
* @param {boolean} [opts.forceScripts] Force scripts on (ignores verified list)
|
|
100
|
+
* @param {Function} [opts.log] Log fn (line-oriented). Defaults to stderr.
|
|
101
|
+
* @returns {Promise<{installed: number, ranScripts: boolean, verified: boolean}>}
|
|
102
|
+
*/
|
|
103
|
+
export async function installPackageDependencies({
|
|
104
|
+
dir,
|
|
105
|
+
packageName = null,
|
|
106
|
+
kind = 'pack',
|
|
107
|
+
forceScripts = false,
|
|
108
|
+
log = null,
|
|
109
|
+
} = {}) {
|
|
110
|
+
const pkgPath = path.join(dir, 'package.json');
|
|
111
|
+
if (!fs.existsSync(pkgPath)) return { installed: 0, ranScripts: false, verified: false };
|
|
112
|
+
|
|
113
|
+
const write = log || ((msg) => process.stderr.write(` ${msg}\n`));
|
|
114
|
+
|
|
115
|
+
// Pi ingredients: migrate peerDependencies → dependencies before install.
|
|
116
|
+
const pkg = kind === 'pi' ? migratePeersForPi(pkgPath) : (() => {
|
|
117
|
+
try { return JSON.parse(fs.readFileSync(pkgPath, 'utf-8')); } catch { return {}; }
|
|
118
|
+
})();
|
|
119
|
+
|
|
120
|
+
const depCount = Object.keys(pkg.dependencies || {}).length;
|
|
121
|
+
if (depCount === 0) return { installed: 0, ranScripts: false, verified: false };
|
|
122
|
+
|
|
123
|
+
const verified = forceScripts || isPackageVerified(packageName);
|
|
124
|
+
const scriptsFlag = verified ? [] : ['--ignore-scripts'];
|
|
125
|
+
const label = kind === 'pi' ? 'pi runtime deps' : 'pack npm deps';
|
|
126
|
+
|
|
127
|
+
write(`${DIM}installing ${depCount} ${label}${verified ? '' : ' (scripts skipped — unverified)'}…${RESET}`);
|
|
128
|
+
await runNpm(
|
|
129
|
+
['install', '--no-audit', '--no-fund', '--legacy-peer-deps', '--silent', ...scriptsFlag],
|
|
130
|
+
{ cwd: dir },
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
if (!verified && packageName) {
|
|
134
|
+
write(`${YELLOW}!${RESET} ${DIM}${packageName} is not in the verified list — postinstall scripts were skipped. Some packages need scripts to build native modules; if the pack fails at first use, re-install with ${RESET}--force-scripts${DIM} once you've reviewed the package.${RESET}`);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return { installed: depCount, ranScripts: verified, verified };
|
|
138
|
+
}
|