dsh-coding-sidebar 1.0.9 → 1.0.10
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/README.md +2 -2
- package/lib/client-editor.js +215 -184
- package/lib/client-registry.js +504 -309
- package/lib/client-terminal.js +210 -179
- package/lib/client.js +507 -312
- package/lib/index.js +63 -0
- package/lib/types/changes-ops.d.ts +31 -0
- package/lib/types/client/SessionLens.d.ts +4 -0
- package/lib/types/client/api.d.ts +10 -0
- package/lib/types/client/locales.d.ts +8 -0
- package/lib/types/client/redact.d.ts +14 -0
- package/package.json +1 -1
- package/src/changes-ops.ts +78 -0
- package/src/client/GitView.tsx +37 -0
- package/src/client/SessionLens.tsx +95 -0
- package/src/client/api.ts +4 -0
- package/src/client/locales-ar.ts +8 -0
- package/src/client/locales-de.ts +8 -0
- package/src/client/locales-fr.ts +8 -0
- package/src/client/locales-hi.ts +8 -0
- package/src/client/locales-id.ts +8 -0
- package/src/client/locales-it.ts +8 -0
- package/src/client/locales-ja.ts +8 -0
- package/src/client/locales-ko.ts +8 -0
- package/src/client/locales-nl.ts +8 -0
- package/src/client/locales-pl.ts +8 -0
- package/src/client/locales-pt.ts +8 -0
- package/src/client/locales-ru.ts +8 -0
- package/src/client/locales-sv.ts +8 -0
- package/src/client/locales-th.ts +8 -0
- package/src/client/locales-tr.ts +8 -0
- package/src/client/locales-vi.ts +8 -0
- package/src/client/locales-zh-HK.ts +8 -0
- package/src/client/locales-zh-MO.ts +8 -0
- package/src/client/locales-zh-TW.ts +8 -0
- package/src/client/locales.ts +16 -0
- package/src/client/redact.ts +39 -0
- package/src/client/sidebar.module.css +115 -0
- package/src/index.ts +13 -0
package/src/client/locales-tr.ts
CHANGED
|
@@ -167,6 +167,14 @@ export const tr: Record<string, string> = {
|
|
|
167
167
|
deleteDescFile: 'Bu dosya kalıcı olarak silinir. Bu işlem geri alınamaz.',
|
|
168
168
|
deleteDescDir: 'Bu dizin ve içeriği kalıcı olarak silinir. Bu işlem geri alınamaz.',
|
|
169
169
|
dismiss: 'Kapat',
|
|
170
|
+
changesSessionGit: 'Git değişiklikleri',
|
|
171
|
+
changesSessionLens: 'Oturum değişiklikleri',
|
|
172
|
+
changesEmpty: 'Bu oturumda dosya işlemi yok',
|
|
173
|
+
changesCount: '{count} dosya işlemi',
|
|
174
|
+
changesRedacted: '[MASKELENDİ]',
|
|
175
|
+
changesBinary: 'İkili dosya — önizleme yok',
|
|
176
|
+
changesPreviewError: 'Önizleme okunamadı: {message}',
|
|
177
|
+
changesLens: 'Görünüm',
|
|
170
178
|
exited: 'Terminal süreci sonlandı',
|
|
171
179
|
noSession: 'Kenar çubuğunu kullanmak için bir oturum seçin',
|
|
172
180
|
pluginNotLoaded: 'Eklenti yüklenmedi; sekme kullanılamıyor:',
|
package/src/client/locales-vi.ts
CHANGED
|
@@ -167,6 +167,14 @@ export const vi: Record<string, string> = {
|
|
|
167
167
|
deleteDescFile: 'Thao tác này sẽ xóa vĩnh viễn tệp. Không thể hoàn tác.',
|
|
168
168
|
deleteDescDir: 'Thao tác này sẽ xóa vĩnh viễn thư mục và mọi thứ bên trong. Không thể hoàn tác.',
|
|
169
169
|
dismiss: 'Đóng',
|
|
170
|
+
changesSessionGit: 'Thay đổi Git',
|
|
171
|
+
changesSessionLens: 'Thay đổi phiên',
|
|
172
|
+
changesEmpty: 'Không có thao tác tệp trong phiên này',
|
|
173
|
+
changesCount: '{count} thao tác tệp',
|
|
174
|
+
changesRedacted: '[ĐÃ CHE]',
|
|
175
|
+
changesBinary: 'Tệp nhị phân — không có xem trước',
|
|
176
|
+
changesPreviewError: 'Lỗi đọc xem trước: {message}',
|
|
177
|
+
changesLens: 'Chế độ xem',
|
|
170
178
|
exited: 'Tiến trình terminal đã thoát',
|
|
171
179
|
noSession: 'Chọn một phiên để dùng thanh bên',
|
|
172
180
|
pluginNotLoaded: 'Plugin chưa tải, tab tạm không khả dụng:',
|
|
@@ -182,6 +182,14 @@ export const zhHK: Record<string, string> = {
|
|
|
182
182
|
deleteDescFile: '將永久刪除該檔案,此操作無法復原。',
|
|
183
183
|
deleteDescDir: '將永久刪除該目錄及其全部內容,此操作無法復原。',
|
|
184
184
|
dismiss: '關閉',
|
|
185
|
+
changesSessionGit: 'Git 變動',
|
|
186
|
+
changesSessionLens: '會話變動',
|
|
187
|
+
changesEmpty: '本會話尚無檔案操作',
|
|
188
|
+
changesCount: '{count} 個檔案操作',
|
|
189
|
+
changesRedacted: '[已脫敏]',
|
|
190
|
+
changesBinary: '二進位檔案,無法預覽',
|
|
191
|
+
changesPreviewError: '預覽讀取失敗:{message}',
|
|
192
|
+
changesLens: 'View',
|
|
185
193
|
exited: '終端程序已退出',
|
|
186
194
|
noSession: '選擇一個工作階段以使用側邊欄',
|
|
187
195
|
pluginNotLoaded: '插件未載入,標籤暫不可用:',
|
|
@@ -182,6 +182,14 @@ export const zhMO: Record<string, string> = {
|
|
|
182
182
|
deleteDescFile: '將永久刪除該檔案,此操作無法復原。',
|
|
183
183
|
deleteDescDir: '將永久刪除該目錄及其全部內容,此操作無法復原。',
|
|
184
184
|
dismiss: '關閉',
|
|
185
|
+
changesSessionGit: 'Git 變動',
|
|
186
|
+
changesSessionLens: '會話變動',
|
|
187
|
+
changesEmpty: '本會話尚無檔案操作',
|
|
188
|
+
changesCount: '{count} 個檔案操作',
|
|
189
|
+
changesRedacted: '[已脫敏]',
|
|
190
|
+
changesBinary: '二進位檔案,無法預覽',
|
|
191
|
+
changesPreviewError: '預覽讀取失敗:{message}',
|
|
192
|
+
changesLens: 'View',
|
|
185
193
|
exited: '終端程序已退出',
|
|
186
194
|
noSession: '選擇一個工作階段以使用側邊欄',
|
|
187
195
|
pluginNotLoaded: '插件未載入,標籤暫不可用:',
|
|
@@ -182,6 +182,14 @@ export const zhTW: Record<string, string> = {
|
|
|
182
182
|
deleteDescFile: '將永久刪除該檔案,此操作無法復原。',
|
|
183
183
|
deleteDescDir: '將永久刪除該目錄及其全部內容,此操作無法復原。',
|
|
184
184
|
dismiss: '關閉',
|
|
185
|
+
changesSessionGit: '偵測 Git 變動',
|
|
186
|
+
changesSessionLens: '會話變動',
|
|
187
|
+
changesEmpty: '本會話尚無檔案操作',
|
|
188
|
+
changesCount: '{count} 個檔案操作',
|
|
189
|
+
changesRedacted: '[已遮蔽]',
|
|
190
|
+
changesBinary: '二進位檔案,無法預覽',
|
|
191
|
+
changesPreviewError: '預覽讀取失敗:{message}',
|
|
192
|
+
changesLens: 'View',
|
|
185
193
|
exited: '終端程序已退出',
|
|
186
194
|
noSession: '選擇一個工作階段以使用側邊欄',
|
|
187
195
|
pluginNotLoaded: '插件未載入,標籤暫不可用:',
|
package/src/client/locales.ts
CHANGED
|
@@ -172,6 +172,14 @@ export const zh = {
|
|
|
172
172
|
deleteDescFile: '将永久删除该文件,此操作不可撤销。',
|
|
173
173
|
deleteDescDir: '将永久删除该目录及其全部内容,此操作不可撤销。',
|
|
174
174
|
dismiss: '关闭',
|
|
175
|
+
changesSessionGit: 'Git 变动',
|
|
176
|
+
changesSessionLens: '会话变动',
|
|
177
|
+
changesEmpty: '本会话尚无文件操作',
|
|
178
|
+
changesCount: '{count} 个文件操作',
|
|
179
|
+
changesRedacted: '[已脱敏]',
|
|
180
|
+
changesBinary: '二进制文件,无法预览',
|
|
181
|
+
changesPreviewError: '预览读取失败:{message}',
|
|
182
|
+
changesLens: '视角',
|
|
175
183
|
exited: '终端进程已退出',
|
|
176
184
|
noSession: '选择一个会话以使用侧边栏',
|
|
177
185
|
pluginNotLoaded: '插件未加载,标签页暂不可用:',
|
|
@@ -521,6 +529,14 @@ export const en: Record<keyof typeof zh, string> = {
|
|
|
521
529
|
deleteDescFile: 'This permanently deletes the file. This cannot be undone.',
|
|
522
530
|
deleteDescDir: 'This permanently deletes the directory and everything inside it. This cannot be undone.',
|
|
523
531
|
dismiss: 'Dismiss',
|
|
532
|
+
changesSessionGit: 'Git changes',
|
|
533
|
+
changesSessionLens: 'Session changes',
|
|
534
|
+
changesEmpty: 'No file operations in this session',
|
|
535
|
+
changesCount: '{count} file operations',
|
|
536
|
+
changesRedacted: '[REDACTED]',
|
|
537
|
+
changesBinary: 'Binary file — no preview',
|
|
538
|
+
changesPreviewError: 'Preview read failed: {message}',
|
|
539
|
+
changesLens: 'Lens',
|
|
524
540
|
exited: 'Terminal process exited',
|
|
525
541
|
noSession: 'Select a conversation to use the sidebar',
|
|
526
542
|
pluginNotLoaded: 'Plugin not loaded; tab unavailable:',
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secret redaction for the session lens' file preview: heuristic masking of
|
|
3
|
+
* credential-shaped strings (API keys, bearer tokens, private key blocks,
|
|
4
|
+
* password assignments) before file content is shown in the sidebar. This
|
|
5
|
+
* layer applies ONLY to the session lens' preview pane — ordinary file reads
|
|
6
|
+
* (editor, untracked diff fallback) never pass through it, so ordinary
|
|
7
|
+
* files' content is untouched.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/** One redaction rule: a pattern plus the replacement it masks matches to. */
|
|
11
|
+
interface RedactRule {
|
|
12
|
+
pattern: RegExp
|
|
13
|
+
replacement: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** The heuristics, applied in order; `$1` keeps the leading keyword where the
|
|
17
|
+
* rule matches an assignment shape. */
|
|
18
|
+
const RULES: RedactRule[] = [
|
|
19
|
+
// PEM private key blocks (the whole block is one secret).
|
|
20
|
+
{ pattern: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g, replacement: '[REDACTED PRIVATE KEY]' },
|
|
21
|
+
// Known token shapes (sk- keys, GitHub tokens, AWS ids, Slack, Google).
|
|
22
|
+
{ pattern: /\b(?:sk-[A-Za-z0-9_-]{16,}|gh[pousr]_[A-Za-z0-9]{16,}|AKIA[0-9A-Z]{16}|xox[abprs]-[A-Za-z0-9-]{10,}|AIza[0-9A-Za-z_-]{30,})\b/g, replacement: '[REDACTED KEY]' },
|
|
23
|
+
// Authorization headers and bearer tokens.
|
|
24
|
+
{ pattern: /([Aa]uthorization\s*:\s*)[^\n"']{4,}/g, replacement: '$1[REDACTED]' },
|
|
25
|
+
{ pattern: /\b([Bb]earer\s+)[A-Za-z0-9._-]{16,}\b/g, replacement: '$1[REDACTED]' },
|
|
26
|
+
// Credential assignment shapes (password/secret/token/api-key = value).
|
|
27
|
+
{ pattern: /(\b[A-Za-z_-]*(?:password|passwd|secret|token|apikey|api_key)[A-Za-z_-]*(?:\s*[:=]\s*))['"]?[^\s"',;){]{3,}/gi, replacement: '$1[REDACTED]' },
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Mask credential-shaped strings in `text`. Best-effort by design: the goal
|
|
32
|
+
* is to keep the common accident (a key echoed into a file the model wrote)
|
|
33
|
+
* out of the sidebar, not to parse every secret format ever shipped.
|
|
34
|
+
*/
|
|
35
|
+
export function redactSecrets(text: string): string {
|
|
36
|
+
let out = text
|
|
37
|
+
for (const rule of RULES) out = out.replace(rule.pattern, rule.replacement)
|
|
38
|
+
return out
|
|
39
|
+
}
|
|
@@ -1696,6 +1696,121 @@ body[data-dsh-sidebar-dragging] .panel {
|
|
|
1696
1696
|
outline: none;
|
|
1697
1697
|
}
|
|
1698
1698
|
|
|
1699
|
+
/* ── The unified changes tab's dual-lens toggle + session lens ─────────── */
|
|
1700
|
+
|
|
1701
|
+
.changesLensToggle {
|
|
1702
|
+
flex: none;
|
|
1703
|
+
display: flex;
|
|
1704
|
+
gap: 2px;
|
|
1705
|
+
padding: 2px;
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1708
|
+
.changesLensTab {
|
|
1709
|
+
flex: 1;
|
|
1710
|
+
height: 24px;
|
|
1711
|
+
border: none;
|
|
1712
|
+
border-radius: 6px;
|
|
1713
|
+
background: transparent;
|
|
1714
|
+
color: var(--dsw-alias-label-secondary);
|
|
1715
|
+
font: var(--dsw-font-xxxs-strong-11);
|
|
1716
|
+
cursor: pointer;
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
.changesLensTab:hover {
|
|
1720
|
+
background: var(--dsw-alias-interactive-bg-hover);
|
|
1721
|
+
}
|
|
1722
|
+
|
|
1723
|
+
.changesLensTabActive,
|
|
1724
|
+
.changesLensTabActive:hover {
|
|
1725
|
+
background: var(--dsw-alias-interactive-bg-active);
|
|
1726
|
+
color: var(--dsw-alias-label-primary);
|
|
1727
|
+
}
|
|
1728
|
+
|
|
1729
|
+
.sessionLens {
|
|
1730
|
+
flex: 1;
|
|
1731
|
+
min-height: 0;
|
|
1732
|
+
overflow-y: auto;
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
.sessionLensBar {
|
|
1736
|
+
display: flex;
|
|
1737
|
+
align-items: center;
|
|
1738
|
+
gap: 6px;
|
|
1739
|
+
height: 26px;
|
|
1740
|
+
padding: 0 4px;
|
|
1741
|
+
}
|
|
1742
|
+
|
|
1743
|
+
.sessionLensCount {
|
|
1744
|
+
flex: 1;
|
|
1745
|
+
min-width: 0;
|
|
1746
|
+
font: var(--dsw-font-xxxs-11);
|
|
1747
|
+
color: var(--dsw-alias-label-tertiary);
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1750
|
+
.sessionLensEmpty {
|
|
1751
|
+
padding: 16px;
|
|
1752
|
+
font: var(--dsw-font-xxs-12);
|
|
1753
|
+
color: var(--dsw-alias-label-tertiary);
|
|
1754
|
+
text-align: center;
|
|
1755
|
+
}
|
|
1756
|
+
|
|
1757
|
+
.sessionLensItem {
|
|
1758
|
+
border-bottom: 1px solid var(--dsw-alias-border-l1);
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1761
|
+
.sessionLensRow {
|
|
1762
|
+
display: flex;
|
|
1763
|
+
align-items: center;
|
|
1764
|
+
gap: 8px;
|
|
1765
|
+
width: 100%;
|
|
1766
|
+
box-sizing: border-box;
|
|
1767
|
+
min-height: 30px;
|
|
1768
|
+
padding: 4px 10px;
|
|
1769
|
+
border: none;
|
|
1770
|
+
border-radius: 6px;
|
|
1771
|
+
background: transparent;
|
|
1772
|
+
color: var(--dsw-alias-label-primary);
|
|
1773
|
+
text-align: left;
|
|
1774
|
+
cursor: pointer;
|
|
1775
|
+
outline: none;
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1778
|
+
.sessionLensRow:hover {
|
|
1779
|
+
background: var(--dsw-alias-interactive-bg-hover);
|
|
1780
|
+
}
|
|
1781
|
+
|
|
1782
|
+
.sessionLensPath {
|
|
1783
|
+
flex: 1;
|
|
1784
|
+
min-width: 0;
|
|
1785
|
+
overflow: hidden;
|
|
1786
|
+
text-overflow: ellipsis;
|
|
1787
|
+
white-space: nowrap;
|
|
1788
|
+
direction: rtl;
|
|
1789
|
+
text-align: left;
|
|
1790
|
+
}
|
|
1791
|
+
|
|
1792
|
+
.sessionLensMeta {
|
|
1793
|
+
flex: none;
|
|
1794
|
+
font: var(--dsw-font-xxxs-11);
|
|
1795
|
+
color: var(--dsw-alias-label-tertiary);
|
|
1796
|
+
}
|
|
1797
|
+
|
|
1798
|
+
.sessionLensPreview {
|
|
1799
|
+
max-height: 260px;
|
|
1800
|
+
overflow: auto;
|
|
1801
|
+
margin: 0 8px 6px;
|
|
1802
|
+
padding: 6px 8px;
|
|
1803
|
+
border: 1px solid var(--dsw-alias-border-l1);
|
|
1804
|
+
border-radius: 6px;
|
|
1805
|
+
background: var(--dsw-alias-bg-layer-1);
|
|
1806
|
+
font-family: var(--ds-font-family-code);
|
|
1807
|
+
font-size: var(--dsw-font-xxxs-11-font-size);
|
|
1808
|
+
line-height: 1.5;
|
|
1809
|
+
color: var(--dsw-alias-label-primary);
|
|
1810
|
+
white-space: pre-wrap;
|
|
1811
|
+
word-break: break-word;
|
|
1812
|
+
}
|
|
1813
|
+
|
|
1699
1814
|
/* The "Agent 正在等待 {needle}" wait banner: a warn-toned strip above the
|
|
1700
1815
|
terminal surface while the model blocks in terminal_wait_for; the needle
|
|
1701
1816
|
ellipsizes inline and the full text rides the title tooltip. */
|
package/src/index.ts
CHANGED
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
} from './config.ts'
|
|
32
32
|
import { parentOf, requireAbsolute, listDirectory, rootLabel } from './fs-tree.ts'
|
|
33
33
|
import { removeWorkspaceEntry, renameWorkspaceEntry, writeWorkspaceUpload } from './fs-operations.ts'
|
|
34
|
+
import { sessionFileOps } from './changes-ops.ts'
|
|
34
35
|
import { ensureWorkspacePath, ensureWorkspaceWritePath } from './path-security.ts'
|
|
35
36
|
import { searchFiles } from './fs-search.ts'
|
|
36
37
|
import { extractFrameAncestors } from './browser-probe.ts'
|
|
@@ -514,6 +515,18 @@ function buildApi(
|
|
|
514
515
|
// exists. Kill is fenced to the owning session by the jobs registry.
|
|
515
516
|
'jobs.output': (payload) => jobsApi.output(payload),
|
|
516
517
|
'jobs.kill': (payload) => jobsApi.kill(payload),
|
|
518
|
+
// The session lens: file operations the model performed in this session,
|
|
519
|
+
// parsed from the session's own event log (read-only replay — the
|
|
520
|
+
// model's job_output cursor is never touched). Cold sessions degrade to
|
|
521
|
+
// an empty list (only live sessions have an in-memory log on this host).
|
|
522
|
+
'changes.ops': async (payload) => {
|
|
523
|
+
const sessionId = requireString(payload, 'sessionId')
|
|
524
|
+
const stored = ctx.sessions.get(sessionId)
|
|
525
|
+
const events = stored?.snapshotEvents !== undefined
|
|
526
|
+
? stored.snapshotEvents() as unknown as Parameters<typeof sessionFileOps>[0]
|
|
527
|
+
: []
|
|
528
|
+
return { ops: sessionFileOps(events) }
|
|
529
|
+
},
|
|
517
530
|
// Subagent live previews: one batch request per refresh; the route folds
|
|
518
531
|
// the newest text/tool activity of every running child in the tree.
|
|
519
532
|
'subagents.live': (payload) => subagentLiveApi.live(payload),
|