dominds 1.2.7 → 1.3.1
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/docs/context-health.md +17 -5
- package/dist/llm/defaults.yaml +9 -0
- package/dist/minds/system-prompt-parts.js +4 -4
- package/dist/server/api-routes.js +577 -80
- package/dist/shared/i18n/driver-messages.js +14 -12
- package/dist/static/assets/{_basePickBy-BMCtwrV7.js → _basePickBy-CmziIRM9.js} +3 -3
- package/dist/static/assets/{_basePickBy-BMCtwrV7.js.map → _basePickBy-CmziIRM9.js.map} +1 -1
- package/dist/static/assets/{_baseUniq-BuyCgJiA.js → _baseUniq-CgDZxcD0.js} +2 -2
- package/dist/static/assets/{_baseUniq-BuyCgJiA.js.map → _baseUniq-CgDZxcD0.js.map} +1 -1
- package/dist/static/assets/{arc-BDuN8lwA.js → arc-Df9rjNNk.js} +2 -2
- package/dist/static/assets/{arc-BDuN8lwA.js.map → arc-Df9rjNNk.js.map} +1 -1
- package/dist/static/assets/{architectureDiagram-VXUJARFQ-C-ekqGAD.js → architectureDiagram-VXUJARFQ-Bif8topC.js} +7 -7
- package/dist/static/assets/{architectureDiagram-VXUJARFQ-C-ekqGAD.js.map → architectureDiagram-VXUJARFQ-Bif8topC.js.map} +1 -1
- package/dist/static/assets/{blockDiagram-VD42YOAC-CgQiNuuQ.js → blockDiagram-VD42YOAC-D9Egoflr.js} +7 -7
- package/dist/static/assets/{blockDiagram-VD42YOAC-CgQiNuuQ.js.map → blockDiagram-VD42YOAC-D9Egoflr.js.map} +1 -1
- package/dist/static/assets/{c4Diagram-YG6GDRKO-DONC39q-.js → c4Diagram-YG6GDRKO-DBf1NeBf.js} +3 -3
- package/dist/static/assets/{c4Diagram-YG6GDRKO-DONC39q-.js.map → c4Diagram-YG6GDRKO-DBf1NeBf.js.map} +1 -1
- package/dist/static/assets/{channel-CJTFwXIG.js → channel-Dc34yAJl.js} +2 -2
- package/dist/static/assets/{channel-CJTFwXIG.js.map → channel-Dc34yAJl.js.map} +1 -1
- package/dist/static/assets/{chunk-4BX2VUAB-NaIy4uLJ.js → chunk-4BX2VUAB-B65G1dJI.js} +2 -2
- package/dist/static/assets/{chunk-4BX2VUAB-NaIy4uLJ.js.map → chunk-4BX2VUAB-B65G1dJI.js.map} +1 -1
- package/dist/static/assets/{chunk-55IACEB6-JUKI_Ayx.js → chunk-55IACEB6-CSDEOGl2.js} +2 -2
- package/dist/static/assets/{chunk-55IACEB6-JUKI_Ayx.js.map → chunk-55IACEB6-CSDEOGl2.js.map} +1 -1
- package/dist/static/assets/{chunk-B4BG7PRW-dIswFJDn.js → chunk-B4BG7PRW-Cb6W3QWJ.js} +5 -5
- package/dist/static/assets/{chunk-B4BG7PRW-dIswFJDn.js.map → chunk-B4BG7PRW-Cb6W3QWJ.js.map} +1 -1
- package/dist/static/assets/{chunk-DI55MBZ5-DU2b_N30.js → chunk-DI55MBZ5-ZAJWeVWH.js} +4 -4
- package/dist/static/assets/{chunk-DI55MBZ5-DU2b_N30.js.map → chunk-DI55MBZ5-ZAJWeVWH.js.map} +1 -1
- package/dist/static/assets/{chunk-FMBD7UC4-BgExcScw.js → chunk-FMBD7UC4-DiwRlImb.js} +2 -2
- package/dist/static/assets/{chunk-FMBD7UC4-BgExcScw.js.map → chunk-FMBD7UC4-DiwRlImb.js.map} +1 -1
- package/dist/static/assets/{chunk-QN33PNHL-bitxyqh7.js → chunk-QN33PNHL-wilj7fb5.js} +2 -2
- package/dist/static/assets/{chunk-QN33PNHL-bitxyqh7.js.map → chunk-QN33PNHL-wilj7fb5.js.map} +1 -1
- package/dist/static/assets/{chunk-QZHKN3VN-Cor8u7DT.js → chunk-QZHKN3VN-DGmviJfR.js} +2 -2
- package/dist/static/assets/{chunk-QZHKN3VN-Cor8u7DT.js.map → chunk-QZHKN3VN-DGmviJfR.js.map} +1 -1
- package/dist/static/assets/{chunk-TZMSLE5B-Aceoxav_.js → chunk-TZMSLE5B-Nm5wTXa_.js} +2 -2
- package/dist/static/assets/{chunk-TZMSLE5B-Aceoxav_.js.map → chunk-TZMSLE5B-Nm5wTXa_.js.map} +1 -1
- package/dist/static/assets/{classDiagram-2ON5EDUG-D1Q6a8Hg.js → classDiagram-2ON5EDUG-BvUbXD6H.js} +6 -6
- package/dist/static/assets/{classDiagram-2ON5EDUG-D1Q6a8Hg.js.map → classDiagram-2ON5EDUG-BvUbXD6H.js.map} +1 -1
- package/dist/static/assets/{classDiagram-v2-WZHVMYZB-D1Q6a8Hg.js → classDiagram-v2-WZHVMYZB-BvUbXD6H.js} +6 -6
- package/dist/static/assets/{classDiagram-v2-WZHVMYZB-D1Q6a8Hg.js.map → classDiagram-v2-WZHVMYZB-BvUbXD6H.js.map} +1 -1
- package/dist/static/assets/{clone-MlWbv1V0.js → clone-0VLS7GaA.js} +2 -2
- package/dist/static/assets/{clone-MlWbv1V0.js.map → clone-0VLS7GaA.js.map} +1 -1
- package/dist/static/assets/{cose-bilkent-S5V4N54A-DWPCXSrn.js → cose-bilkent-S5V4N54A-anaPs-75.js} +2 -2
- package/dist/static/assets/{cose-bilkent-S5V4N54A-DWPCXSrn.js.map → cose-bilkent-S5V4N54A-anaPs-75.js.map} +1 -1
- package/dist/static/assets/{dagre-6UL2VRFP-C8ptQ9V3.js → dagre-6UL2VRFP-YwdsZ11r.js} +7 -7
- package/dist/static/assets/{dagre-6UL2VRFP-C8ptQ9V3.js.map → dagre-6UL2VRFP-YwdsZ11r.js.map} +1 -1
- package/dist/static/assets/{diagram-PSM6KHXK-Bgf1FqkE.js → diagram-PSM6KHXK-5KQCf3h2.js} +8 -8
- package/dist/static/assets/{diagram-PSM6KHXK-Bgf1FqkE.js.map → diagram-PSM6KHXK-5KQCf3h2.js.map} +1 -1
- package/dist/static/assets/{diagram-QEK2KX5R-BZ5xzofU.js → diagram-QEK2KX5R-Mf24XxZL.js} +7 -7
- package/dist/static/assets/{diagram-QEK2KX5R-BZ5xzofU.js.map → diagram-QEK2KX5R-Mf24XxZL.js.map} +1 -1
- package/dist/static/assets/{diagram-S2PKOQOG-Dwp47T9I.js → diagram-S2PKOQOG-DyQjD4D5.js} +7 -7
- package/dist/static/assets/{diagram-S2PKOQOG-Dwp47T9I.js.map → diagram-S2PKOQOG-DyQjD4D5.js.map} +1 -1
- package/dist/static/assets/{erDiagram-Q2GNP2WA-Cx4weIHl.js → erDiagram-Q2GNP2WA-CEzTKw1u.js} +5 -5
- package/dist/static/assets/{erDiagram-Q2GNP2WA-Cx4weIHl.js.map → erDiagram-Q2GNP2WA-CEzTKw1u.js.map} +1 -1
- package/dist/static/assets/{flowDiagram-NV44I4VS-vNUuIeRk.js → flowDiagram-NV44I4VS-DT821XSz.js} +6 -6
- package/dist/static/assets/{flowDiagram-NV44I4VS-vNUuIeRk.js.map → flowDiagram-NV44I4VS-DT821XSz.js.map} +1 -1
- package/dist/static/assets/{ganttDiagram-JELNMOA3-BEfozJAr.js → ganttDiagram-JELNMOA3-DlmeVsGg.js} +3 -3
- package/dist/static/assets/{ganttDiagram-JELNMOA3-BEfozJAr.js.map → ganttDiagram-JELNMOA3-DlmeVsGg.js.map} +1 -1
- package/dist/static/assets/{gitGraphDiagram-V2S2FVAM-eHxwc3d9.js → gitGraphDiagram-V2S2FVAM-yAfyBG_d.js} +8 -8
- package/dist/static/assets/{gitGraphDiagram-V2S2FVAM-eHxwc3d9.js.map → gitGraphDiagram-V2S2FVAM-yAfyBG_d.js.map} +1 -1
- package/dist/static/assets/{graph-C6a6uAok.js → graph-BYv8vyWe.js} +3 -3
- package/dist/static/assets/{graph-C6a6uAok.js.map → graph-BYv8vyWe.js.map} +1 -1
- package/dist/static/assets/{index-D3TQbAKh.js → index-DMbwqOm6.js} +140 -61
- package/dist/static/assets/{index-D3TQbAKh.js.map → index-DMbwqOm6.js.map} +1 -1
- package/dist/static/assets/{infoDiagram-HS3SLOUP-CX0NiId3.js → infoDiagram-HS3SLOUP-DaadramQ.js} +6 -6
- package/dist/static/assets/{infoDiagram-HS3SLOUP-CX0NiId3.js.map → infoDiagram-HS3SLOUP-DaadramQ.js.map} +1 -1
- package/dist/static/assets/{journeyDiagram-XKPGCS4Q-C1IepPZ-.js → journeyDiagram-XKPGCS4Q-ftN8hxu3.js} +5 -5
- package/dist/static/assets/{journeyDiagram-XKPGCS4Q-C1IepPZ-.js.map → journeyDiagram-XKPGCS4Q-ftN8hxu3.js.map} +1 -1
- package/dist/static/assets/{kanban-definition-3W4ZIXB7-uMNX4Z1W.js → kanban-definition-3W4ZIXB7-BIXETQ-C.js} +3 -3
- package/dist/static/assets/{kanban-definition-3W4ZIXB7-uMNX4Z1W.js.map → kanban-definition-3W4ZIXB7-BIXETQ-C.js.map} +1 -1
- package/dist/static/assets/{layout-CpE3kk5z.js → layout-OTbrj0Ye.js} +5 -5
- package/dist/static/assets/{layout-CpE3kk5z.js.map → layout-OTbrj0Ye.js.map} +1 -1
- package/dist/static/assets/{linear-DV8laXr9.js → linear-CVfOC669.js} +2 -2
- package/dist/static/assets/{linear-DV8laXr9.js.map → linear-CVfOC669.js.map} +1 -1
- package/dist/static/assets/{mindmap-definition-VGOIOE7T-CKjgVM9S.js → mindmap-definition-VGOIOE7T-D2zv5uI9.js} +4 -4
- package/dist/static/assets/{mindmap-definition-VGOIOE7T-CKjgVM9S.js.map → mindmap-definition-VGOIOE7T-D2zv5uI9.js.map} +1 -1
- package/dist/static/assets/{pieDiagram-ADFJNKIX-BBonlNyT.js → pieDiagram-ADFJNKIX-DaUXTsv7.js} +8 -8
- package/dist/static/assets/{pieDiagram-ADFJNKIX-BBonlNyT.js.map → pieDiagram-ADFJNKIX-DaUXTsv7.js.map} +1 -1
- package/dist/static/assets/{quadrantDiagram-AYHSOK5B-BTI8HbBu.js → quadrantDiagram-AYHSOK5B-B7O2wPX9.js} +3 -3
- package/dist/static/assets/{quadrantDiagram-AYHSOK5B-BTI8HbBu.js.map → quadrantDiagram-AYHSOK5B-B7O2wPX9.js.map} +1 -1
- package/dist/static/assets/{requirementDiagram-UZGBJVZJ-ZtSr9Q5R.js → requirementDiagram-UZGBJVZJ-DpauVPY1.js} +4 -4
- package/dist/static/assets/{requirementDiagram-UZGBJVZJ-ZtSr9Q5R.js.map → requirementDiagram-UZGBJVZJ-DpauVPY1.js.map} +1 -1
- package/dist/static/assets/{sankeyDiagram-TZEHDZUN-DibLVGzg.js → sankeyDiagram-TZEHDZUN-B3Hbfvad.js} +2 -2
- package/dist/static/assets/{sankeyDiagram-TZEHDZUN-DibLVGzg.js.map → sankeyDiagram-TZEHDZUN-B3Hbfvad.js.map} +1 -1
- package/dist/static/assets/{sequenceDiagram-WL72ISMW-qXatfzVt.js → sequenceDiagram-WL72ISMW-KdbWByWT.js} +4 -4
- package/dist/static/assets/{sequenceDiagram-WL72ISMW-qXatfzVt.js.map → sequenceDiagram-WL72ISMW-KdbWByWT.js.map} +1 -1
- package/dist/static/assets/{stateDiagram-FKZM4ZOC-7fgxCQHo.js → stateDiagram-FKZM4ZOC-yDOCVezC.js} +9 -9
- package/dist/static/assets/{stateDiagram-FKZM4ZOC-7fgxCQHo.js.map → stateDiagram-FKZM4ZOC-yDOCVezC.js.map} +1 -1
- package/dist/static/assets/{stateDiagram-v2-4FDKWEC3-DcWlOAnF.js → stateDiagram-v2-4FDKWEC3-CpCJhvQO.js} +5 -5
- package/dist/static/assets/{stateDiagram-v2-4FDKWEC3-DcWlOAnF.js.map → stateDiagram-v2-4FDKWEC3-CpCJhvQO.js.map} +1 -1
- package/dist/static/assets/{timeline-definition-IT6M3QCI-iX2MRdpY.js → timeline-definition-IT6M3QCI-CHxuEjhV.js} +3 -3
- package/dist/static/assets/{timeline-definition-IT6M3QCI-iX2MRdpY.js.map → timeline-definition-IT6M3QCI-CHxuEjhV.js.map} +1 -1
- package/dist/static/assets/{treemap-GDKQZRPO-AVRnyXu1.js → treemap-GDKQZRPO-Bsqu3wIy.js} +5 -5
- package/dist/static/assets/{treemap-GDKQZRPO-AVRnyXu1.js.map → treemap-GDKQZRPO-Bsqu3wIy.js.map} +1 -1
- package/dist/static/assets/{xychartDiagram-PRI3JC2R-DVYEo5aJ.js → xychartDiagram-PRI3JC2R-RZy33lFF.js} +3 -3
- package/dist/static/assets/{xychartDiagram-PRI3JC2R-DVYEo5aJ.js.map → xychartDiagram-PRI3JC2R-RZy33lFF.js.map} +1 -1
- package/dist/static/index.html +1 -1
- package/dist/tools/ctrl.js +3 -3
- package/dist/tools/prompts/control/en/index.md +5 -4
- package/dist/tools/prompts/control/en/principles.md +11 -7
- package/dist/tools/prompts/control/en/tools.md +19 -0
- package/dist/tools/prompts/control/zh/index.md +5 -4
- package/dist/tools/prompts/control/zh/principles.md +11 -7
- package/dist/tools/prompts/control/zh/tools.md +19 -0
- package/package.json +3 -3
|
@@ -330,7 +330,7 @@ async function handleWorkspaceFilePreviewPage(req, res, pathname) {
|
|
|
330
330
|
<head>
|
|
331
331
|
<meta charset="utf-8" />
|
|
332
332
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
333
|
-
<title>Dominds
|
|
333
|
+
<title>Dominds Workspace Preview</title>
|
|
334
334
|
<link
|
|
335
335
|
rel="stylesheet"
|
|
336
336
|
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/github-dark.min.css"
|
|
@@ -343,7 +343,9 @@ async function handleWorkspaceFilePreviewPage(req, res, pathname) {
|
|
|
343
343
|
background: #0f172a;
|
|
344
344
|
color: #e2e8f0;
|
|
345
345
|
}
|
|
346
|
-
|
|
346
|
+
a { color: #93c5fd; text-decoration: none; }
|
|
347
|
+
a:hover { text-decoration: underline; }
|
|
348
|
+
.wrap { max-width: 1280px; margin: 0 auto; padding: 16px; }
|
|
347
349
|
.panel {
|
|
348
350
|
border: 1px solid #334155;
|
|
349
351
|
border-radius: 10px;
|
|
@@ -379,78 +381,238 @@ async function handleWorkspaceFilePreviewPage(req, res, pathname) {
|
|
|
379
381
|
background: #1f1010;
|
|
380
382
|
color: #fecaca;
|
|
381
383
|
}
|
|
382
|
-
|
|
383
|
-
margin: 0;
|
|
384
|
+
.code-wrap, .dir-wrap {
|
|
384
385
|
border-radius: 8px;
|
|
385
386
|
border: 1px solid #334155;
|
|
386
387
|
background: #0b1220;
|
|
387
388
|
overflow: auto;
|
|
388
389
|
max-height: calc(100vh - 190px);
|
|
389
390
|
}
|
|
390
|
-
code {
|
|
391
|
+
.code-view {
|
|
392
|
+
min-width: max-content;
|
|
393
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
394
|
+
font-size: 12px;
|
|
395
|
+
line-height: 1.6;
|
|
396
|
+
}
|
|
397
|
+
.code-line {
|
|
398
|
+
display: grid;
|
|
399
|
+
grid-template-columns: auto 1fr;
|
|
400
|
+
}
|
|
401
|
+
.code-line:hover {
|
|
402
|
+
background: rgba(148, 163, 184, 0.08);
|
|
403
|
+
}
|
|
404
|
+
.code-line.target-line {
|
|
405
|
+
background: rgba(245, 158, 11, 0.14);
|
|
406
|
+
}
|
|
407
|
+
.line-no {
|
|
408
|
+
position: sticky;
|
|
409
|
+
left: 0;
|
|
410
|
+
padding: 0 12px;
|
|
411
|
+
min-width: 56px;
|
|
412
|
+
box-sizing: border-box;
|
|
413
|
+
text-align: right;
|
|
414
|
+
user-select: none;
|
|
415
|
+
-webkit-user-select: none;
|
|
416
|
+
-moz-user-select: none;
|
|
417
|
+
color: #64748b;
|
|
418
|
+
background: #0f172a;
|
|
419
|
+
border-right: 1px solid #1e293b;
|
|
420
|
+
}
|
|
421
|
+
.code-line.target-line .line-no {
|
|
422
|
+
color: #fbbf24;
|
|
423
|
+
background: #1c1917;
|
|
424
|
+
}
|
|
425
|
+
.line-content {
|
|
426
|
+
display: block;
|
|
427
|
+
margin: 0;
|
|
428
|
+
padding: 0 12px;
|
|
429
|
+
white-space: pre;
|
|
430
|
+
tab-size: 2;
|
|
431
|
+
}
|
|
432
|
+
.line-content:empty::after {
|
|
433
|
+
content: ' ';
|
|
434
|
+
}
|
|
435
|
+
.target-col {
|
|
436
|
+
background: #f59e0b;
|
|
437
|
+
color: #111827;
|
|
438
|
+
border-radius: 3px;
|
|
439
|
+
box-shadow: 0 0 0 1px rgba(245, 158, 11, 0.45);
|
|
440
|
+
}
|
|
441
|
+
.target-col-caret {
|
|
442
|
+
display: inline-block;
|
|
443
|
+
width: 2px;
|
|
444
|
+
min-height: 1.2em;
|
|
445
|
+
vertical-align: text-bottom;
|
|
446
|
+
background: #f59e0b;
|
|
447
|
+
box-shadow: 0 0 0 1px rgba(245, 158, 11, 0.35);
|
|
448
|
+
}
|
|
449
|
+
.dir-list {
|
|
450
|
+
width: 100%;
|
|
451
|
+
border-collapse: collapse;
|
|
452
|
+
font-size: 13px;
|
|
453
|
+
}
|
|
454
|
+
.dir-list thead th {
|
|
455
|
+
position: sticky;
|
|
456
|
+
top: 0;
|
|
457
|
+
z-index: 1;
|
|
458
|
+
background: #111827;
|
|
459
|
+
color: #94a3b8;
|
|
460
|
+
font-weight: 600;
|
|
461
|
+
text-align: left;
|
|
462
|
+
border-bottom: 1px solid #334155;
|
|
463
|
+
}
|
|
464
|
+
.dir-list th, .dir-list td {
|
|
465
|
+
padding: 8px 10px;
|
|
466
|
+
border-bottom: 1px solid #1e293b;
|
|
467
|
+
vertical-align: top;
|
|
468
|
+
}
|
|
469
|
+
.dir-list tbody tr:hover {
|
|
470
|
+
background: rgba(148, 163, 184, 0.08);
|
|
471
|
+
}
|
|
472
|
+
.entry-name {
|
|
473
|
+
display: inline-flex;
|
|
474
|
+
align-items: center;
|
|
475
|
+
gap: 8px;
|
|
476
|
+
min-width: 0;
|
|
477
|
+
}
|
|
478
|
+
.entry-icon {
|
|
479
|
+
width: 1.2em;
|
|
480
|
+
text-align: center;
|
|
481
|
+
color: #cbd5e1;
|
|
482
|
+
}
|
|
483
|
+
.entry-label {
|
|
484
|
+
word-break: break-word;
|
|
485
|
+
}
|
|
486
|
+
.entry-type, .entry-size {
|
|
487
|
+
white-space: nowrap;
|
|
488
|
+
color: #94a3b8;
|
|
489
|
+
}
|
|
490
|
+
.entry-note {
|
|
491
|
+
color: #94a3b8;
|
|
492
|
+
font-size: 12px;
|
|
493
|
+
word-break: break-word;
|
|
494
|
+
}
|
|
495
|
+
.entry-note.warn {
|
|
496
|
+
color: #fca5a5;
|
|
497
|
+
}
|
|
391
498
|
</style>
|
|
392
499
|
</head>
|
|
393
500
|
<body>
|
|
394
501
|
<div class="wrap">
|
|
395
502
|
<div class="panel">
|
|
396
503
|
<div class="head">
|
|
397
|
-
<div id="
|
|
398
|
-
<div id="
|
|
504
|
+
<div id="preview-path" class="path">Loading...</div>
|
|
505
|
+
<div id="preview-meta" class="meta"></div>
|
|
399
506
|
</div>
|
|
400
507
|
<div class="body">
|
|
401
|
-
<div id="status" class="status">Loading
|
|
402
|
-
<
|
|
508
|
+
<div id="status" class="status">Loading workspace entry...</div>
|
|
509
|
+
<div id="code-wrap" class="code-wrap" style="display:none;">
|
|
510
|
+
<div id="code-view" class="code-view"></div>
|
|
511
|
+
</div>
|
|
512
|
+
<div id="dir-wrap" class="dir-wrap" style="display:none;">
|
|
513
|
+
<table class="dir-list">
|
|
514
|
+
<thead>
|
|
515
|
+
<tr>
|
|
516
|
+
<th>Name</th>
|
|
517
|
+
<th>Type</th>
|
|
518
|
+
<th>Size</th>
|
|
519
|
+
<th>Notes</th>
|
|
520
|
+
</tr>
|
|
521
|
+
</thead>
|
|
522
|
+
<tbody id="dir-body"></tbody>
|
|
523
|
+
</table>
|
|
524
|
+
</div>
|
|
403
525
|
</div>
|
|
404
526
|
</div>
|
|
405
527
|
</div>
|
|
406
528
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js"></script>
|
|
407
529
|
<script>
|
|
408
530
|
(function () {
|
|
409
|
-
var
|
|
410
|
-
var
|
|
531
|
+
var pathEl = document.getElementById('preview-path');
|
|
532
|
+
var metaEl = document.getElementById('preview-meta');
|
|
411
533
|
var statusEl = document.getElementById('status');
|
|
412
534
|
var codeWrapEl = document.getElementById('code-wrap');
|
|
413
|
-
var
|
|
414
|
-
|
|
535
|
+
var codeViewEl = document.getElementById('code-view');
|
|
536
|
+
var dirWrapEl = document.getElementById('dir-wrap');
|
|
537
|
+
var dirBodyEl = document.getElementById('dir-body');
|
|
538
|
+
if (
|
|
539
|
+
!pathEl ||
|
|
540
|
+
!metaEl ||
|
|
541
|
+
!statusEl ||
|
|
542
|
+
!codeWrapEl ||
|
|
543
|
+
!codeViewEl ||
|
|
544
|
+
!dirWrapEl ||
|
|
545
|
+
!dirBodyEl
|
|
546
|
+
) return;
|
|
415
547
|
|
|
416
548
|
function setError(message) {
|
|
417
549
|
statusEl.classList.add('err');
|
|
418
550
|
statusEl.textContent = message;
|
|
551
|
+
statusEl.style.display = 'block';
|
|
419
552
|
codeWrapEl.style.display = 'none';
|
|
553
|
+
dirWrapEl.style.display = 'none';
|
|
420
554
|
}
|
|
421
555
|
|
|
422
|
-
function
|
|
556
|
+
function resetViews() {
|
|
557
|
+
statusEl.classList.remove('err');
|
|
558
|
+
codeWrapEl.style.display = 'none';
|
|
559
|
+
dirWrapEl.style.display = 'none';
|
|
560
|
+
codeViewEl.innerHTML = '';
|
|
561
|
+
dirBodyEl.innerHTML = '';
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
function showReady() {
|
|
565
|
+
statusEl.style.display = 'none';
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
function escapeHtml(value) {
|
|
569
|
+
return String(value)
|
|
570
|
+
.replace(/&/g, '&')
|
|
571
|
+
.replace(/</g, '<')
|
|
572
|
+
.replace(/>/g, '>')
|
|
573
|
+
.replace(/"/g, '"')
|
|
574
|
+
.replace(/'/g, ''');
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
function normalizeRelativePath(input, allowRoot) {
|
|
423
578
|
if (typeof input !== 'string') return null;
|
|
424
|
-
if (input.length < 1) return null;
|
|
425
579
|
if (input.indexOf('\\\\') >= 0) return null;
|
|
426
580
|
if (/[\\u0000]/.test(input)) return null;
|
|
427
581
|
if (input.charAt(0) === '/') return null;
|
|
428
|
-
var parts = input.split('/')
|
|
429
|
-
|
|
582
|
+
var parts = input.split('/');
|
|
583
|
+
var normalized = [];
|
|
430
584
|
for (var i = 0; i < parts.length; i += 1) {
|
|
431
585
|
var seg = parts[i];
|
|
432
|
-
if (seg
|
|
586
|
+
if (!seg || seg === '.') continue;
|
|
587
|
+
if (seg === '..') {
|
|
588
|
+
if (normalized.length < 1) return null;
|
|
589
|
+
normalized.pop();
|
|
590
|
+
continue;
|
|
591
|
+
}
|
|
592
|
+
normalized.push(seg);
|
|
433
593
|
}
|
|
434
|
-
return
|
|
594
|
+
if (normalized.length < 1) return allowRoot ? '' : null;
|
|
595
|
+
return normalized.join('/');
|
|
435
596
|
}
|
|
436
597
|
|
|
437
598
|
function parseRelativePathFromLocation() {
|
|
438
599
|
var pathname = window.location.pathname || '';
|
|
600
|
+
if (pathname === '/f' || pathname === '/f/') return '';
|
|
439
601
|
if (!pathname.startsWith('/f/')) return null;
|
|
440
602
|
var tail = pathname.slice('/f/'.length);
|
|
441
|
-
if (tail.length < 1) return
|
|
603
|
+
if (tail.length < 1) return '';
|
|
442
604
|
var rawParts = tail.split('/');
|
|
443
|
-
if (rawParts.some(function (s) { return s.length < 1; })) return null;
|
|
444
605
|
var decodedParts = [];
|
|
445
606
|
for (var i = 0; i < rawParts.length; i += 1) {
|
|
607
|
+
var rawPart = rawParts[i];
|
|
608
|
+
if (rawPart.length < 1) continue;
|
|
446
609
|
var decoded;
|
|
447
|
-
try { decoded = decodeURIComponent(
|
|
448
|
-
if (decoded.length < 1)
|
|
610
|
+
try { decoded = decodeURIComponent(rawPart); } catch { return null; }
|
|
611
|
+
if (decoded.length < 1) continue;
|
|
449
612
|
if (decoded.indexOf('/') >= 0 || decoded.indexOf('\\\\') >= 0 || /[\\u0000]/.test(decoded)) return null;
|
|
450
|
-
if (decoded === '.' || decoded === '..') return null;
|
|
451
613
|
decodedParts.push(decoded);
|
|
452
614
|
}
|
|
453
|
-
return normalizeRelativePath(decodedParts.join('/'));
|
|
615
|
+
return normalizeRelativePath(decodedParts.join('/'), true);
|
|
454
616
|
}
|
|
455
617
|
|
|
456
618
|
function parsePositiveInt(raw) {
|
|
@@ -474,15 +636,206 @@ async function handleWorkspaceFilePreviewPage(req, res, pathname) {
|
|
|
474
636
|
}
|
|
475
637
|
|
|
476
638
|
function formatBytes(size) {
|
|
477
|
-
if (typeof size !== 'number' || !Number.isFinite(size) || size < 0) return
|
|
639
|
+
if (typeof size !== 'number' || !Number.isFinite(size) || size < 0) return '';
|
|
478
640
|
if (size < 1024) return String(size) + ' B';
|
|
479
641
|
if (size < 1024 * 1024) return (size / 1024).toFixed(1) + ' KiB';
|
|
480
642
|
return (size / (1024 * 1024)).toFixed(1) + ' MiB';
|
|
481
643
|
}
|
|
482
644
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
645
|
+
function buildPreviewHref(relativePath) {
|
|
646
|
+
if (typeof relativePath !== 'string') return '/f';
|
|
647
|
+
var normalized = normalizeRelativePath(relativePath, true);
|
|
648
|
+
if (normalized === null || normalized.length < 1) return '/f';
|
|
649
|
+
return '/f/' + normalized.split('/').map(function (segment) {
|
|
650
|
+
return encodeURIComponent(segment);
|
|
651
|
+
}).join('/');
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
function parentPathOf(relativePath) {
|
|
655
|
+
if (relativePath.length < 1) return null;
|
|
656
|
+
var parts = relativePath.split('/');
|
|
657
|
+
parts.pop();
|
|
658
|
+
return parts.join('/');
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
function renderHighlightedSegment(raw, lang) {
|
|
662
|
+
if (typeof raw !== 'string' || raw.length < 1) return '';
|
|
663
|
+
if (
|
|
664
|
+
window.hljs &&
|
|
665
|
+
typeof window.hljs.highlight === 'function' &&
|
|
666
|
+
typeof window.hljs.getLanguage === 'function' &&
|
|
667
|
+
lang !== 'plaintext' &&
|
|
668
|
+
window.hljs.getLanguage(lang)
|
|
669
|
+
) {
|
|
670
|
+
return window.hljs.highlight(raw, { language: lang, ignoreIllegals: true }).value;
|
|
671
|
+
}
|
|
672
|
+
return escapeHtml(raw);
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
function buildLineHtml(rawLine, lang, isTargetLine, targetColumn) {
|
|
676
|
+
if (!isTargetLine || targetColumn === null) {
|
|
677
|
+
return rawLine.length > 0 ? renderHighlightedSegment(rawLine, lang) : '';
|
|
678
|
+
}
|
|
679
|
+
var clampedColumn = targetColumn;
|
|
680
|
+
if (clampedColumn < 1) clampedColumn = 1;
|
|
681
|
+
if (clampedColumn > rawLine.length + 1) clampedColumn = rawLine.length + 1;
|
|
682
|
+
var splitIndex = clampedColumn - 1;
|
|
683
|
+
var before = rawLine.slice(0, splitIndex);
|
|
684
|
+
var after = rawLine.slice(splitIndex + 1);
|
|
685
|
+
if (splitIndex >= rawLine.length) {
|
|
686
|
+
return renderHighlightedSegment(rawLine, lang) + '<span class="target-col-caret" aria-hidden="true"></span>';
|
|
687
|
+
}
|
|
688
|
+
var targetChar = rawLine.charAt(splitIndex);
|
|
689
|
+
return (
|
|
690
|
+
renderHighlightedSegment(before, lang) +
|
|
691
|
+
'<span class="target-col">' + renderHighlightedSegment(targetChar, lang) + '</span>' +
|
|
692
|
+
renderHighlightedSegment(after, lang)
|
|
693
|
+
);
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
function renderFile(raw, lang, targetLine, targetColumn) {
|
|
697
|
+
codeViewEl.innerHTML = '';
|
|
698
|
+
var lines = raw.split('\\n');
|
|
699
|
+
var fragment = document.createDocumentFragment();
|
|
700
|
+
var selectedLineEl = null;
|
|
701
|
+
for (var i = 0; i < lines.length; i += 1) {
|
|
702
|
+
var lineNumber = i + 1;
|
|
703
|
+
var rawLine = lines[i];
|
|
704
|
+
var rowEl = document.createElement('div');
|
|
705
|
+
rowEl.className = 'code-line' + (lineNumber === targetLine ? ' target-line' : '');
|
|
706
|
+
rowEl.setAttribute('data-line', String(lineNumber));
|
|
707
|
+
|
|
708
|
+
var lineNoEl = document.createElement('span');
|
|
709
|
+
lineNoEl.className = 'line-no';
|
|
710
|
+
lineNoEl.textContent = String(lineNumber);
|
|
711
|
+
lineNoEl.setAttribute('aria-hidden', 'true');
|
|
712
|
+
|
|
713
|
+
var lineContentEl = document.createElement('span');
|
|
714
|
+
lineContentEl.className = 'line-content hljs';
|
|
715
|
+
lineContentEl.innerHTML = buildLineHtml(rawLine, lang, lineNumber === targetLine, targetColumn);
|
|
716
|
+
|
|
717
|
+
rowEl.appendChild(lineNoEl);
|
|
718
|
+
rowEl.appendChild(lineContentEl);
|
|
719
|
+
fragment.appendChild(rowEl);
|
|
720
|
+
if (lineNumber === targetLine) {
|
|
721
|
+
selectedLineEl = rowEl;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
codeViewEl.appendChild(fragment);
|
|
725
|
+
codeWrapEl.style.display = 'block';
|
|
726
|
+
if (selectedLineEl && typeof selectedLineEl.scrollIntoView === 'function') {
|
|
727
|
+
selectedLineEl.scrollIntoView({ block: 'center' });
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
function createEntryLink(relativePath, label, icon) {
|
|
732
|
+
var link = document.createElement('a');
|
|
733
|
+
link.href = buildPreviewHref(relativePath);
|
|
734
|
+
link.className = 'entry-name';
|
|
735
|
+
|
|
736
|
+
var iconEl = document.createElement('span');
|
|
737
|
+
iconEl.className = 'entry-icon';
|
|
738
|
+
iconEl.textContent = icon;
|
|
739
|
+
|
|
740
|
+
var labelEl = document.createElement('span');
|
|
741
|
+
labelEl.className = 'entry-label';
|
|
742
|
+
labelEl.textContent = label;
|
|
743
|
+
|
|
744
|
+
link.appendChild(iconEl);
|
|
745
|
+
link.appendChild(labelEl);
|
|
746
|
+
return link;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
function describeEntryNote(entry) {
|
|
750
|
+
if (!entry || entry.isSymlink !== true) return '';
|
|
751
|
+
if (typeof entry.symlinkTarget !== 'string' || entry.symlinkTarget.length < 1) return 'symlink';
|
|
752
|
+
return 'symlink → ' + entry.symlinkTarget;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
function createDirRow(entry) {
|
|
756
|
+
var row = document.createElement('tr');
|
|
757
|
+
|
|
758
|
+
var nameCell = document.createElement('td');
|
|
759
|
+
var label = entry.name + (entry.kind === 'directory' ? '/' : '');
|
|
760
|
+
var icon = entry.kind === 'directory' ? '📁' : entry.kind === 'file' ? '📄' : '⛔';
|
|
761
|
+
if (entry.kind === 'directory' || entry.kind === 'file') {
|
|
762
|
+
nameCell.appendChild(createEntryLink(entry.path, label, icon));
|
|
763
|
+
} else {
|
|
764
|
+
var nameWrap = document.createElement('span');
|
|
765
|
+
nameWrap.className = 'entry-name';
|
|
766
|
+
var iconEl = document.createElement('span');
|
|
767
|
+
iconEl.className = 'entry-icon';
|
|
768
|
+
iconEl.textContent = icon;
|
|
769
|
+
var labelEl = document.createElement('span');
|
|
770
|
+
labelEl.className = 'entry-label';
|
|
771
|
+
labelEl.textContent = label;
|
|
772
|
+
nameWrap.appendChild(iconEl);
|
|
773
|
+
nameWrap.appendChild(labelEl);
|
|
774
|
+
nameCell.appendChild(nameWrap);
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
var typeCell = document.createElement('td');
|
|
778
|
+
typeCell.className = 'entry-type';
|
|
779
|
+
typeCell.textContent = entry.kind;
|
|
780
|
+
|
|
781
|
+
var sizeCell = document.createElement('td');
|
|
782
|
+
sizeCell.className = 'entry-size';
|
|
783
|
+
sizeCell.textContent = typeof entry.size === 'number' ? formatBytes(entry.size) : '';
|
|
784
|
+
|
|
785
|
+
var noteCell = document.createElement('td');
|
|
786
|
+
var note = describeEntryNote(entry);
|
|
787
|
+
if (typeof entry.resolvedKind === 'string' && entry.resolvedKind !== entry.kind) {
|
|
788
|
+
note = note
|
|
789
|
+
? note + ' | ' + entry.resolvedKind
|
|
790
|
+
: entry.resolvedKind;
|
|
791
|
+
}
|
|
792
|
+
if (note) {
|
|
793
|
+
var noteEl = document.createElement('div');
|
|
794
|
+
noteEl.className = 'entry-note' + (entry.kind === 'other' ? ' warn' : '');
|
|
795
|
+
noteEl.textContent = note;
|
|
796
|
+
noteCell.appendChild(noteEl);
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
row.appendChild(nameCell);
|
|
800
|
+
row.appendChild(typeCell);
|
|
801
|
+
row.appendChild(sizeCell);
|
|
802
|
+
row.appendChild(noteCell);
|
|
803
|
+
return row;
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
function renderDirectory(currentPath, entries) {
|
|
807
|
+
dirBodyEl.innerHTML = '';
|
|
808
|
+
var fragment = document.createDocumentFragment();
|
|
809
|
+
|
|
810
|
+
var parentPath = parentPathOf(currentPath);
|
|
811
|
+
if (parentPath !== null) {
|
|
812
|
+
var parentRow = document.createElement('tr');
|
|
813
|
+
var nameCell = document.createElement('td');
|
|
814
|
+
nameCell.appendChild(createEntryLink(parentPath, '../', '↩'));
|
|
815
|
+
var typeCell = document.createElement('td');
|
|
816
|
+
typeCell.className = 'entry-type';
|
|
817
|
+
typeCell.textContent = 'directory';
|
|
818
|
+
var sizeCell = document.createElement('td');
|
|
819
|
+
sizeCell.className = 'entry-size';
|
|
820
|
+
var noteCell = document.createElement('td');
|
|
821
|
+
parentRow.appendChild(nameCell);
|
|
822
|
+
parentRow.appendChild(typeCell);
|
|
823
|
+
parentRow.appendChild(sizeCell);
|
|
824
|
+
parentRow.appendChild(noteCell);
|
|
825
|
+
fragment.appendChild(parentRow);
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
for (var i = 0; i < entries.length; i += 1) {
|
|
829
|
+
fragment.appendChild(createDirRow(entries[i]));
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
dirBodyEl.appendChild(fragment);
|
|
833
|
+
dirWrapEl.style.display = 'block';
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
var previewPath = parseRelativePathFromLocation();
|
|
837
|
+
if (previewPath === null) {
|
|
838
|
+
pathEl.textContent = 'Invalid preview path';
|
|
486
839
|
setError('Invalid preview path. Expected /f/<rtws-relative-path> and no ..');
|
|
487
840
|
return;
|
|
488
841
|
}
|
|
@@ -497,13 +850,11 @@ async function handleWorkspaceFilePreviewPage(req, res, pathname) {
|
|
|
497
850
|
(typeof authFromUrl === 'string' && authFromUrl.trim() !== '' ? authFromUrl.trim() : null) ||
|
|
498
851
|
(typeof authFromStorage === 'string' && authFromStorage.trim() !== '' ? authFromStorage.trim() : null);
|
|
499
852
|
|
|
500
|
-
|
|
501
|
-
var meta = [];
|
|
853
|
+
pathEl.textContent = previewPath.length > 0 ? previewPath : '.';
|
|
502
854
|
if (line !== null) {
|
|
503
|
-
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
fileMetaEl.textContent = meta.join(' | ');
|
|
855
|
+
metaEl.textContent = 'Line ' + String(line) + (column !== null ? ':' + String(column) : '');
|
|
856
|
+
} else {
|
|
857
|
+
metaEl.textContent = '';
|
|
507
858
|
}
|
|
508
859
|
|
|
509
860
|
var headers = { Accept: 'application/json' };
|
|
@@ -511,7 +862,8 @@ async function handleWorkspaceFilePreviewPage(req, res, pathname) {
|
|
|
511
862
|
headers['Authorization'] = 'Bearer ' + token;
|
|
512
863
|
}
|
|
513
864
|
|
|
514
|
-
|
|
865
|
+
resetViews();
|
|
866
|
+
fetch('/api/workspace/entry?path=' + encodeURIComponent(previewPath), {
|
|
515
867
|
method: 'GET',
|
|
516
868
|
headers: headers,
|
|
517
869
|
cache: 'no-store'
|
|
@@ -522,7 +874,7 @@ async function handleWorkspaceFilePreviewPage(req, res, pathname) {
|
|
|
522
874
|
});
|
|
523
875
|
})
|
|
524
876
|
.then(function (result) {
|
|
525
|
-
if (!result.ok || !result.payload || result.payload.success !== true || typeof result.payload.
|
|
877
|
+
if (!result.ok || !result.payload || result.payload.success !== true || typeof result.payload.kind !== 'string') {
|
|
526
878
|
var msg = result.payload && typeof result.payload.error === 'string' && result.payload.error !== ''
|
|
527
879
|
? result.payload.error
|
|
528
880
|
: ('Request failed: HTTP ' + String(result.status));
|
|
@@ -530,24 +882,37 @@ async function handleWorkspaceFilePreviewPage(req, res, pathname) {
|
|
|
530
882
|
return;
|
|
531
883
|
}
|
|
532
884
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
if (line !== null) metaItems.push('Line ' + String(line) + (column !== null ? ':' + String(column) : ''));
|
|
539
|
-
metaItems.push(lang);
|
|
540
|
-
if (sizeText !== null) metaItems.push(sizeText);
|
|
541
|
-
fileMetaEl.textContent = metaItems.join(' | ');
|
|
885
|
+
showReady();
|
|
886
|
+
pathEl.textContent =
|
|
887
|
+
typeof result.payload.path === 'string' && result.payload.path.length > 0
|
|
888
|
+
? result.payload.path
|
|
889
|
+
: '.';
|
|
542
890
|
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
891
|
+
if (result.payload.kind === 'file' && typeof result.payload.raw === 'string') {
|
|
892
|
+
var lang = detectLang(typeof result.payload.path === 'string' ? result.payload.path : previewPath);
|
|
893
|
+
var sizeText = formatBytes(result.payload.size);
|
|
894
|
+
var metaItems = [];
|
|
895
|
+
if (line !== null) metaItems.push('Line ' + String(line) + (column !== null ? ':' + String(column) : ''));
|
|
896
|
+
metaItems.push(lang);
|
|
897
|
+
if (sizeText) metaItems.push(sizeText);
|
|
898
|
+
metaEl.textContent = metaItems.join(' | ');
|
|
899
|
+
renderFile(result.payload.raw, lang, line, column);
|
|
900
|
+
return;
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
if (result.payload.kind === 'directory' && Array.isArray(result.payload.entries)) {
|
|
904
|
+
metaEl.textContent = 'directory | ' + String(result.payload.entries.length) + ' entries';
|
|
905
|
+
renderDirectory(
|
|
906
|
+
typeof result.payload.path === 'string' ? result.payload.path : previewPath,
|
|
907
|
+
result.payload.entries,
|
|
908
|
+
);
|
|
909
|
+
return;
|
|
547
910
|
}
|
|
911
|
+
|
|
912
|
+
setError('Invalid preview payload');
|
|
548
913
|
})
|
|
549
914
|
.catch(function (err) {
|
|
550
|
-
var msg = err && typeof err.message === 'string' ? err.message : 'Failed to load
|
|
915
|
+
var msg = err && typeof err.message === 'string' ? err.message : 'Failed to load workspace entry';
|
|
551
916
|
setError(msg);
|
|
552
917
|
});
|
|
553
918
|
})();
|
|
@@ -781,9 +1146,9 @@ async function handleApiRoute(req, res, pathname, context) {
|
|
|
781
1146
|
if (pathname === '/api/docs/read' && req.method === 'GET') {
|
|
782
1147
|
return await handleReadDocsMarkdown(req, res);
|
|
783
1148
|
}
|
|
784
|
-
// Read workspace file content for markdown
|
|
785
|
-
if (pathname === '/api/workspace/
|
|
786
|
-
return await
|
|
1149
|
+
// Read workspace file or directory content for markdown preview links.
|
|
1150
|
+
if (pathname === '/api/workspace/entry' && req.method === 'GET') {
|
|
1151
|
+
return await handleReadWorkspaceEntry(req, res);
|
|
787
1152
|
}
|
|
788
1153
|
if (pathname === '/api/snippets/builtin' && req.method === 'GET') {
|
|
789
1154
|
const payload = await (0, snippets_routes_1.handleGetBuiltinSnippets)();
|
|
@@ -1035,10 +1400,8 @@ function isWithinResolvedRoot(targetAbsPath, rootAbsPath) {
|
|
|
1035
1400
|
return true;
|
|
1036
1401
|
return targetAbsPath.startsWith(ensurePathSuffixSeparator(rootAbsPath));
|
|
1037
1402
|
}
|
|
1038
|
-
function
|
|
1403
|
+
function normalizeRtwsRelativePath(input, options) {
|
|
1039
1404
|
const trimmed = input.trim();
|
|
1040
|
-
if (trimmed.length < 1)
|
|
1041
|
-
return null;
|
|
1042
1405
|
if (trimmed.includes('\0'))
|
|
1043
1406
|
return null;
|
|
1044
1407
|
if (trimmed.includes('\\'))
|
|
@@ -1046,8 +1409,11 @@ function normalizeRtwsRelativeFilePath(input) {
|
|
|
1046
1409
|
if (path.posix.isAbsolute(trimmed))
|
|
1047
1410
|
return null;
|
|
1048
1411
|
const normalized = path.posix.normalize(trimmed);
|
|
1049
|
-
if (normalized
|
|
1412
|
+
if (normalized === '..')
|
|
1050
1413
|
return null;
|
|
1414
|
+
if (normalized === '.' || normalized.length < 1) {
|
|
1415
|
+
return options.allowRoot ? '' : null;
|
|
1416
|
+
}
|
|
1051
1417
|
if (normalized.startsWith('../'))
|
|
1052
1418
|
return null;
|
|
1053
1419
|
if (normalized.includes('/../'))
|
|
@@ -1060,50 +1426,181 @@ function normalizeRtwsRelativeFilePath(input) {
|
|
|
1060
1426
|
}
|
|
1061
1427
|
return normalized;
|
|
1062
1428
|
}
|
|
1063
|
-
async function
|
|
1064
|
-
const
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
return
|
|
1429
|
+
async function getWorkspaceRootRealAbs() {
|
|
1430
|
+
const workspaceRootAbs = path.resolve(process.cwd());
|
|
1431
|
+
try {
|
|
1432
|
+
return await promises_1.default.realpath(workspaceRootAbs);
|
|
1433
|
+
}
|
|
1434
|
+
catch {
|
|
1435
|
+
return workspaceRootAbs;
|
|
1070
1436
|
}
|
|
1437
|
+
}
|
|
1438
|
+
function classifyWorkspaceEntryKind(followStat) {
|
|
1439
|
+
if (followStat.isDirectory())
|
|
1440
|
+
return 'directory';
|
|
1441
|
+
if (followStat.isFile())
|
|
1442
|
+
return 'file';
|
|
1443
|
+
return 'other';
|
|
1444
|
+
}
|
|
1445
|
+
async function resolveWorkspacePreviewPath(pathRel) {
|
|
1071
1446
|
const workspaceRootAbs = path.resolve(process.cwd());
|
|
1072
|
-
const
|
|
1447
|
+
const workspaceRootRealAbs = await getWorkspaceRootRealAbs();
|
|
1448
|
+
const candidateAbsPath = pathRel.length < 1 ? workspaceRootAbs : path.resolve(workspaceRootAbs, pathRel);
|
|
1073
1449
|
if (!isWithinResolvedRoot(candidateAbsPath, workspaceRootAbs)) {
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1450
|
+
const error = new Error(`Workspace preview path escaped rtws: ${pathRel}`);
|
|
1451
|
+
error.code = 'OUTSIDE_RTWS';
|
|
1452
|
+
throw error;
|
|
1453
|
+
}
|
|
1454
|
+
const resolvedAbsPath = await promises_1.default.realpath(candidateAbsPath);
|
|
1455
|
+
if (!isWithinResolvedRoot(resolvedAbsPath, workspaceRootRealAbs)) {
|
|
1456
|
+
const error = new Error(`Workspace preview path resolved outside rtws: ${pathRel}`);
|
|
1457
|
+
error.code = 'OUTSIDE_RTWS';
|
|
1458
|
+
throw error;
|
|
1459
|
+
}
|
|
1460
|
+
return {
|
|
1461
|
+
workspaceRootAbs,
|
|
1462
|
+
workspaceRootRealAbs,
|
|
1463
|
+
candidateAbsPath,
|
|
1464
|
+
resolvedAbsPath,
|
|
1465
|
+
};
|
|
1466
|
+
}
|
|
1467
|
+
async function listWorkspaceDirectoryEntries(params) {
|
|
1468
|
+
const entryNames = await promises_1.default.readdir(params.dirAbsPath);
|
|
1469
|
+
const entries = await Promise.all(entryNames.map(async (name) => {
|
|
1470
|
+
const childRelPath = params.pathRel.length < 1 ? name : `${params.pathRel}/${name}`;
|
|
1471
|
+
const childAbsPath = path.resolve(process.cwd(), childRelPath);
|
|
1472
|
+
const lstat = await promises_1.default.lstat(childAbsPath);
|
|
1473
|
+
const isSymlink = lstat.isSymbolicLink();
|
|
1474
|
+
let symlinkTarget;
|
|
1475
|
+
if (isSymlink) {
|
|
1476
|
+
try {
|
|
1477
|
+
symlinkTarget = await promises_1.default.readlink(childAbsPath);
|
|
1478
|
+
}
|
|
1479
|
+
catch {
|
|
1480
|
+
symlinkTarget = undefined;
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
let resolvedKind;
|
|
1484
|
+
let kind;
|
|
1485
|
+
let size;
|
|
1486
|
+
try {
|
|
1487
|
+
const resolvedChildAbsPath = await promises_1.default.realpath(childAbsPath);
|
|
1488
|
+
if (!isWithinResolvedRoot(resolvedChildAbsPath, params.workspaceRootRealAbs)) {
|
|
1489
|
+
resolvedKind = 'outside_rtws';
|
|
1490
|
+
kind = 'other';
|
|
1491
|
+
}
|
|
1492
|
+
else {
|
|
1493
|
+
const followStat = await promises_1.default.stat(childAbsPath);
|
|
1494
|
+
resolvedKind = classifyWorkspaceEntryKind(followStat);
|
|
1495
|
+
kind = resolvedKind;
|
|
1496
|
+
size = followStat.isFile() ? followStat.size : undefined;
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
catch (error) {
|
|
1500
|
+
if (getErrorCode(error) === 'ENOENT') {
|
|
1501
|
+
resolvedKind = 'broken';
|
|
1502
|
+
kind = 'other';
|
|
1503
|
+
}
|
|
1504
|
+
else {
|
|
1505
|
+
throw error;
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
return {
|
|
1509
|
+
name,
|
|
1510
|
+
path: childRelPath,
|
|
1511
|
+
kind,
|
|
1512
|
+
resolvedKind,
|
|
1513
|
+
size,
|
|
1514
|
+
isSymlink,
|
|
1515
|
+
symlinkTarget,
|
|
1516
|
+
};
|
|
1517
|
+
}));
|
|
1518
|
+
entries.sort((a, b) => {
|
|
1519
|
+
const rank = (kind) => {
|
|
1520
|
+
switch (kind) {
|
|
1521
|
+
case 'directory':
|
|
1522
|
+
return 0;
|
|
1523
|
+
case 'file':
|
|
1524
|
+
return 1;
|
|
1525
|
+
case 'other':
|
|
1526
|
+
return 2;
|
|
1527
|
+
}
|
|
1528
|
+
};
|
|
1529
|
+
const rankDiff = rank(a.kind) - rank(b.kind);
|
|
1530
|
+
if (rankDiff !== 0)
|
|
1531
|
+
return rankDiff;
|
|
1532
|
+
return a.name.localeCompare(b.name);
|
|
1533
|
+
});
|
|
1534
|
+
return entries;
|
|
1535
|
+
}
|
|
1536
|
+
async function handleReadWorkspaceEntry(req, res) {
|
|
1537
|
+
const urlObj = new URL(req.url ?? '', 'http://127.0.0.1');
|
|
1538
|
+
const pathRaw = urlObj.searchParams.get('path');
|
|
1539
|
+
const pathRel = typeof pathRaw === 'string' ? normalizeRtwsRelativePath(pathRaw, { allowRoot: true }) : '';
|
|
1540
|
+
if (pathRel === null) {
|
|
1541
|
+
respondJson(res, 400, { success: false, error: 'Invalid workspace path' });
|
|
1079
1542
|
return true;
|
|
1080
1543
|
}
|
|
1081
1544
|
try {
|
|
1082
|
-
const
|
|
1545
|
+
const resolved = await resolveWorkspacePreviewPath(pathRel);
|
|
1546
|
+
const stat = await promises_1.default.stat(resolved.candidateAbsPath);
|
|
1547
|
+
if (stat.isDirectory()) {
|
|
1548
|
+
const entries = await listWorkspaceDirectoryEntries({
|
|
1549
|
+
pathRel,
|
|
1550
|
+
dirAbsPath: resolved.resolvedAbsPath,
|
|
1551
|
+
workspaceRootRealAbs: resolved.workspaceRootRealAbs,
|
|
1552
|
+
});
|
|
1553
|
+
respondJson(res, 200, {
|
|
1554
|
+
success: true,
|
|
1555
|
+
kind: 'directory',
|
|
1556
|
+
path: pathRel,
|
|
1557
|
+
entries,
|
|
1558
|
+
});
|
|
1559
|
+
return true;
|
|
1560
|
+
}
|
|
1083
1561
|
if (!stat.isFile()) {
|
|
1084
|
-
respondJson(res, 400, {
|
|
1562
|
+
respondJson(res, 400, {
|
|
1563
|
+
success: false,
|
|
1564
|
+
error: 'Path must resolve to a file or directory',
|
|
1565
|
+
path: pathRel,
|
|
1566
|
+
});
|
|
1085
1567
|
return true;
|
|
1086
1568
|
}
|
|
1087
1569
|
if (stat.size > WORKSPACE_FILE_PREVIEW_MAX_BYTES) {
|
|
1088
1570
|
respondJson(res, 413, {
|
|
1089
1571
|
success: false,
|
|
1090
1572
|
error: `File too large for preview (max ${WORKSPACE_FILE_PREVIEW_MAX_BYTES} bytes)`,
|
|
1091
|
-
path:
|
|
1573
|
+
path: pathRel,
|
|
1092
1574
|
size: stat.size,
|
|
1093
1575
|
});
|
|
1094
1576
|
return true;
|
|
1095
1577
|
}
|
|
1096
|
-
const raw = await promises_1.default.readFile(candidateAbsPath, 'utf-8');
|
|
1097
|
-
respondJson(res, 200, {
|
|
1578
|
+
const raw = await promises_1.default.readFile(resolved.candidateAbsPath, 'utf-8');
|
|
1579
|
+
respondJson(res, 200, {
|
|
1580
|
+
success: true,
|
|
1581
|
+
kind: 'file',
|
|
1582
|
+
path: pathRel,
|
|
1583
|
+
raw,
|
|
1584
|
+
size: stat.size,
|
|
1585
|
+
});
|
|
1098
1586
|
return true;
|
|
1099
1587
|
}
|
|
1100
1588
|
catch (error) {
|
|
1101
|
-
|
|
1102
|
-
|
|
1589
|
+
const code = getErrorCode(error);
|
|
1590
|
+
if (code === 'ENOENT') {
|
|
1591
|
+
respondJson(res, 404, { success: false, error: 'Path not found', path: pathRel });
|
|
1592
|
+
return true;
|
|
1593
|
+
}
|
|
1594
|
+
if (code === 'OUTSIDE_RTWS') {
|
|
1595
|
+
respondJson(res, 403, {
|
|
1596
|
+
success: false,
|
|
1597
|
+
error: 'Path resolves outside rtws',
|
|
1598
|
+
path: pathRel,
|
|
1599
|
+
});
|
|
1103
1600
|
return true;
|
|
1104
1601
|
}
|
|
1105
|
-
log.error('Failed to read workspace
|
|
1106
|
-
respondJson(res, 500, { success: false, error: 'Failed to read workspace
|
|
1602
|
+
log.error('Failed to read workspace entry', error, { path: pathRel });
|
|
1603
|
+
respondJson(res, 500, { success: false, error: 'Failed to read workspace entry' });
|
|
1107
1604
|
return true;
|
|
1108
1605
|
}
|
|
1109
1606
|
}
|