dsh-mobile-access 0.0.0-stage → 1.0.0

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/html.js ADDED
@@ -0,0 +1,275 @@
1
+ /** Self-contained, dependency-free HTML for the public pairing route. */
2
+ const CSP = [
3
+ "default-src 'none'",
4
+ "style-src 'unsafe-inline'",
5
+ "form-action 'self'",
6
+ "base-uri 'none'",
7
+ "frame-ancestors 'none'",
8
+ ].join('; ');
9
+ export function contentSecurityPolicy() {
10
+ return CSP;
11
+ }
12
+ export function escapeHtml(value) {
13
+ return value
14
+ .replaceAll('&', '&')
15
+ .replaceAll('<', '&lt;')
16
+ .replaceAll('>', '&gt;')
17
+ .replaceAll('"', '&quot;')
18
+ .replaceAll("'", '&#39;');
19
+ }
20
+ function shell(title, body) {
21
+ return `<!doctype html>
22
+ <html lang="zh-CN">
23
+ <head>
24
+ <meta charset="utf-8">
25
+ <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
26
+ <meta name="format-detection" content="telephone=no">
27
+ <meta name="robots" content="noindex, nofollow">
28
+ <meta name="color-scheme" content="light dark">
29
+ <meta name="theme-color" content="#f4f6fa" media="(prefers-color-scheme: light)">
30
+ <meta name="theme-color" content="#0b0f14" media="(prefers-color-scheme: dark)">
31
+ <meta name="apple-mobile-web-app-capable" content="yes">
32
+ <meta name="apple-mobile-web-app-title" content="DSH 手机访问">
33
+ <title>${escapeHtml(title)}</title>
34
+ <style>
35
+ :root {
36
+ color-scheme: light dark;
37
+ --bg: #f4f6fa;
38
+ --card: #ffffff;
39
+ --text: #111827;
40
+ --muted: #5f6b7a;
41
+ --line: #dfe4ec;
42
+ --brand: #2f5bea;
43
+ --brand-ink: #ffffff;
44
+ --danger: #b42318;
45
+ --danger-bg: #fff2f0;
46
+ --ok: #087443;
47
+ --ok-bg: #eaf8f0;
48
+ --shadow: 0 18px 50px rgba(20, 33, 61, .12);
49
+ }
50
+ @media (prefers-color-scheme: dark) {
51
+ :root {
52
+ --bg: #0b0f14;
53
+ --card: #141b24;
54
+ --text: #f3f6fa;
55
+ --muted: #a5b2c2;
56
+ --line: #2a3542;
57
+ --brand: #7aa2ff;
58
+ --brand-ink: #08101f;
59
+ --danger: #ff9a90;
60
+ --danger-bg: #2a1514;
61
+ --ok: #69d9a3;
62
+ --ok-bg: #10251b;
63
+ --shadow: 0 18px 50px rgba(0, 0, 0, .35);
64
+ }
65
+ }
66
+ * { box-sizing: border-box; }
67
+ html, body { margin: 0; min-height: 100%; }
68
+ body {
69
+ min-height: 100vh;
70
+ min-height: 100svh;
71
+ display: grid;
72
+ place-items: center;
73
+ padding: max(20px, env(safe-area-inset-top)) max(20px, env(safe-area-inset-right)) max(20px, env(safe-area-inset-bottom)) max(20px, env(safe-area-inset-left));
74
+ background: radial-gradient(circle at 20% 0%, color-mix(in srgb, var(--brand) 16%, transparent), transparent 42%), var(--bg);
75
+ color: var(--text);
76
+ font: 16px/1.55 -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", sans-serif;
77
+ -webkit-text-size-adjust: 100%;
78
+ }
79
+ .card {
80
+ width: 100%;
81
+ max-width: 430px;
82
+ padding: 24px;
83
+ border: 1px solid var(--line);
84
+ border-radius: 22px;
85
+ background: var(--card);
86
+ box-shadow: var(--shadow);
87
+ }
88
+ .brand {
89
+ display: flex;
90
+ align-items: center;
91
+ gap: 10px;
92
+ margin-bottom: 18px;
93
+ color: var(--muted);
94
+ font-size: 13px;
95
+ font-weight: 650;
96
+ letter-spacing: .08em;
97
+ text-transform: uppercase;
98
+ }
99
+ .mark {
100
+ display: grid;
101
+ place-items: center;
102
+ width: 30px;
103
+ height: 30px;
104
+ border-radius: 10px;
105
+ background: var(--brand);
106
+ color: var(--brand-ink);
107
+ font-size: 15px;
108
+ font-weight: 800;
109
+ }
110
+ h1 { margin: 0 0 8px; font-size: 25px; line-height: 1.2; letter-spacing: -.02em; }
111
+ p { margin: 0 0 14px; color: var(--muted); }
112
+ .status {
113
+ display: inline-flex;
114
+ align-items: center;
115
+ gap: 7px;
116
+ min-height: 32px;
117
+ padding: 5px 11px;
118
+ margin: 2px 0 18px;
119
+ border: 1px solid var(--line);
120
+ border-radius: 999px;
121
+ color: var(--muted);
122
+ font-size: 13px;
123
+ font-weight: 650;
124
+ }
125
+ .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--muted); }
126
+ .dot.ok { background: var(--ok); }
127
+ .dot.warn { background: #d97706; }
128
+ .dot.bad { background: var(--danger); }
129
+ .error, .locked {
130
+ padding: 12px 13px;
131
+ margin: 0 0 16px;
132
+ border-radius: 12px;
133
+ background: var(--danger-bg);
134
+ color: var(--danger);
135
+ font-size: 14px;
136
+ font-weight: 600;
137
+ }
138
+ label { display: block; margin-bottom: 8px; font-size: 14px; font-weight: 700; }
139
+ input {
140
+ width: 100%;
141
+ min-height: 54px;
142
+ padding: 10px 12px;
143
+ border: 1px solid var(--line);
144
+ border-radius: 13px;
145
+ background: var(--bg);
146
+ color: var(--text);
147
+ font: 600 20px/1.2 ui-monospace, "SF Mono", Menlo, monospace;
148
+ font-variant-numeric: tabular-nums;
149
+ letter-spacing: .18em;
150
+ text-align: center;
151
+ outline: none;
152
+ -webkit-appearance: none;
153
+ }
154
+ input:focus { border-color: var(--brand); box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 24%, transparent); }
155
+ button, .button {
156
+ display: flex;
157
+ align-items: center;
158
+ justify-content: center;
159
+ width: 100%;
160
+ min-height: 50px;
161
+ margin-top: 12px;
162
+ padding: 12px 16px;
163
+ border: 0;
164
+ border-radius: 13px;
165
+ background: var(--brand);
166
+ color: var(--brand-ink);
167
+ font: 700 17px/1.2 -apple-system, BlinkMacSystemFont, "SF Pro Text", sans-serif;
168
+ text-decoration: none;
169
+ cursor: pointer;
170
+ -webkit-tap-highlight-color: transparent;
171
+ }
172
+ button:active, .button:active { transform: translateY(1px); }
173
+ .button.secondary {
174
+ margin-top: 10px;
175
+ border: 1px solid var(--line);
176
+ background: transparent;
177
+ color: var(--text);
178
+ }
179
+ .steps {
180
+ margin: 20px 0 0;
181
+ padding: 16px 0 0;
182
+ border-top: 1px solid var(--line);
183
+ counter-reset: step;
184
+ list-style: none;
185
+ }
186
+ .steps li {
187
+ position: relative;
188
+ margin: 0 0 11px;
189
+ padding-left: 30px;
190
+ color: var(--muted);
191
+ font-size: 14px;
192
+ }
193
+ .steps li:last-child { margin-bottom: 0; }
194
+ .steps li::before {
195
+ counter-increment: step;
196
+ content: counter(step);
197
+ position: absolute;
198
+ top: -1px;
199
+ left: 0;
200
+ display: grid;
201
+ place-items: center;
202
+ width: 21px;
203
+ height: 21px;
204
+ border-radius: 7px;
205
+ background: color-mix(in srgb, var(--brand) 14%, transparent);
206
+ color: var(--brand);
207
+ font-size: 12px;
208
+ font-weight: 800;
209
+ }
210
+ .meta {
211
+ margin-top: 16px;
212
+ color: var(--muted);
213
+ font-size: 12px;
214
+ line-height: 1.5;
215
+ word-break: break-word;
216
+ }
217
+ .meta code { font-family: ui-monospace, "SF Mono", Menlo, monospace; }
218
+ @media (max-width: 360px) {
219
+ .card { padding: 20px; border-radius: 18px; }
220
+ h1 { font-size: 23px; }
221
+ }
222
+ </style>
223
+ </head>
224
+ <body>
225
+ <main class="card" role="main">
226
+ ${body}
227
+ </main>
228
+ </body>
229
+ </html>`;
230
+ }
231
+ export function renderPairingPage(options) {
232
+ const status = options.locked
233
+ ? { text: '配对码已锁定', dot: 'bad' }
234
+ : options.expired
235
+ ? { text: '配对码已过期', dot: 'warn' }
236
+ : { text: '等待配对', dot: 'ok' };
237
+ const error = options.error === undefined
238
+ ? ''
239
+ : `<div class="error" role="alert" aria-live="polite">${escapeHtml(options.error)}</div>`;
240
+ const form = options.locked || options.expired
241
+ ? `<p class="locked">${options.locked
242
+ ? '请回到 Mac 的 DSH,打开左侧栏「手机访问」,点击「刷新配对码」,然后重新加载本页。'
243
+ : '配对码只在 10 分钟内有效。请回到 Mac 的 DSH 面板刷新配对码,然后重新加载本页。'}</p>
244
+ <a class="button" href="/m">重新加载配对页</a>`
245
+ : `<form method="post" action="/m" accept-charset="utf-8" novalidate>
246
+ <input type="hidden" name="nonce" value="${escapeHtml(options.nonce)}">
247
+ <label for="pairing-code">配对码</label>
248
+ <input id="pairing-code" name="code" type="text" inputmode="numeric" autocomplete="off" autocapitalize="off" autocorrect="off" spellcheck="false" maxlength="12" pattern="[0-9 ]*" placeholder="1234 5678" aria-describedby="pairing-help" required>
249
+ <button type="submit">连接 DSH</button>
250
+ </form>
251
+ <p class="meta" id="pairing-help">配对码显示在 Mac 的 DSH 左侧栏「手机访问」面板中,共 ${options.codeLength} 位数字。输入错误次数过多会锁定,需在 Mac 面板刷新。</p>`;
252
+ return shell(`DSH 手机访问 · ${status.text}`, `
253
+ <div class="brand"><span class="mark">D</span><span>DeepSeek Harness</span></div>
254
+ <h1>手机访问</h1>
255
+ <p>在同一局域网内,用这台 iPhone 或 iPad 安全打开 Mac 上的官方 DSH Web。</p>
256
+ <div class="status"><span class="dot ${status.dot}"></span>${status.text}</div>
257
+ ${error}
258
+ ${form}
259
+ <ol class="steps">
260
+ <li>在 Mac 的 DSH 左侧栏点击「手机访问」。</li>
261
+ <li>输入面板上显示的 ${options.codeLength} 位配对码。</li>
262
+ <li>配对成功后会自动进入 DSH,可再「添加到主屏幕」。</li>
263
+ </ol>
264
+ <p class="meta">当前地址:<code>${escapeHtml(options.host)}</code><br>本页面不会把配对码写入 URL 或浏览器历史记录。配对码有效至 ${escapeHtml(new Date(options.expiresAt).toLocaleTimeString('zh-CN', { hour12: false }))}(Mac 本地时间)。</p>
265
+ `);
266
+ }
267
+ export function renderMessagePage(options) {
268
+ return shell(options.title, `
269
+ <div class="brand"><span class="mark">D</span><span>DeepSeek Harness</span></div>
270
+ <h1>${escapeHtml(options.title)}</h1>
271
+ <div class="error" role="alert">${escapeHtml(options.message)}</div>
272
+ ${options.detail === undefined ? '' : `<p class="meta">${escapeHtml(options.detail)}</p>`}
273
+ <a class="button secondary" href="/m">返回配对页</a>
274
+ `);
275
+ }
package/icon.svg ADDED
@@ -0,0 +1,14 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="Mobile Access">
2
+ <defs>
3
+ <linearGradient id="g" x1="8" y1="4" x2="56" y2="60" gradientUnits="userSpaceOnUse">
4
+ <stop stop-color="#6ea8ff"/>
5
+ <stop offset="1" stop-color="#2f5bea"/>
6
+ </linearGradient>
7
+ </defs>
8
+ <rect width="64" height="64" rx="16" fill="url(#g)"/>
9
+ <rect x="19" y="11" width="26" height="42" rx="6" fill="none" stroke="#fff" stroke-width="3"/>
10
+ <path d="M28 16h8" stroke="#fff" stroke-width="3" stroke-linecap="round"/>
11
+ <circle cx="32" cy="46" r="1.8" fill="#fff"/>
12
+ <path d="M24.5 27.5a10.5 10.5 0 0 1 15 0M28 31a5.7 5.7 0 0 1 8 0" fill="none" stroke="#fff" stroke-width="2.6" stroke-linecap="round"/>
13
+ <circle cx="32" cy="35" r="1.5" fill="#fff"/>
14
+ </svg>
package/index.js ADDED
@@ -0,0 +1,20 @@
1
+ /** Host half of the independent `dsh-mobile-access` bundle. */
2
+ import { registerMobileAccess } from './routes.js';
3
+ import { PairingService } from './service.js';
4
+ export const name = 'dsh-mobile-access';
5
+ export const inject = ['webServer', 'connection'];
6
+ /**
7
+ * Register exactly three additive routes. No existing route, auth record,
8
+ * profile setting, layout component, or client root is read or replaced.
9
+ */
10
+ export function apply(ctx) {
11
+ const service = new PairingService();
12
+ ctx.effect(() => {
13
+ const dispose = registerMobileAccess(ctx, service);
14
+ ctx.logger?.info?.('[mobile-access] routes registered');
15
+ return () => {
16
+ dispose();
17
+ ctx.logger?.info?.('[mobile-access] routes disposed');
18
+ };
19
+ }, 'mobile-access routes');
20
+ }
package/locale/en.json ADDED
@@ -0,0 +1,79 @@
1
+ {
2
+ "meta": {
3
+ "title": "Mobile Access",
4
+ "description": "Pair an iPhone or iPad on the same LAN so Safari can securely open the official DeepSeek Harness Web GUI."
5
+ },
6
+ "ui": {
7
+ "panel": "Mobile Access",
8
+ "title": "Mobile Access",
9
+ "subtitle": "Open this Mac’s official DSH from an iPhone or iPad on the same LAN.",
10
+ "statusLoading": "Reading pairing state",
11
+ "statusReady": "Ready",
12
+ "statusLoopback": "Loopback only",
13
+ "statusLocked": "Code locked",
14
+ "statusExpired": "Code expired",
15
+ "statusError": "State unavailable",
16
+ "errorTitle": "Could not read pairing state",
17
+ "retry": "Reload",
18
+ "urlLabel": "Address for iPhone",
19
+ "urlHint": "Type this address into Safari on the iPhone.",
20
+ "codeLabel": "Pairing code",
21
+ "codeHint": "Enter these 8 digits on the iPhone pairing page.",
22
+ "copyLink": "Copy address",
23
+ "copyCode": "Copy code",
24
+ "copied": "Copied",
25
+ "copyFailed": "Copy failed; select the text manually",
26
+ "rotate": "New code",
27
+ "rotating": "Rotating…",
28
+ "toastRotated": "Pairing code rotated",
29
+ "rotateFailed": "Rotation failed; try again",
30
+ "expiry": "Valid until",
31
+ "expiresIn": "Remaining",
32
+ "expired": "Expired",
33
+ "locked": "Locked",
34
+ "never": "Never",
35
+ "lastPaired": "Last paired",
36
+ "pairCount": "Successful pairings",
37
+ "stepsTitle": "Connect from iPhone",
38
+ "step1": "Put the iPhone and the Mac on the same Wi-Fi, without client isolation.",
39
+ "step2": "Open the address in Safari, enter the code, and tap “Connect DSH”.",
40
+ "step3": "After pairing, use Safari Share → “Add to Home Screen”.",
41
+ "diagnosticsTitle": "Connection diagnostics",
42
+ "bindLabel": "Mac listener",
43
+ "deviceLabel": "This device",
44
+ "deviceIOS": "iPhone / iPad",
45
+ "deviceOther": "Computer or other device",
46
+ "launchMode": "Launch mode",
47
+ "standaloneYes": "Launched from Home Screen",
48
+ "standaloneNo": "Browser tab",
49
+ "secureYes": "Secure context",
50
+ "secureNo": "HTTP insecure context (clipboard fallback enabled)",
51
+ "loopbackTitle": "The iPhone cannot connect yet",
52
+ "loopbackBody": "DSH is listening on loopback only. Set Host webserver.host to this Mac’s LAN IPv4 address, then restart DSH manually.",
53
+ "httpTitle": "About HTTP",
54
+ "httpBody": "This is plain-HTTP LAN access. DSH already handles UUIDs and most copy buttons; this plugin adds a fallback for older direct navigator.clipboard calls on iPhone.",
55
+ "selftestTitle": "Browser capability self-test",
56
+ "selftestRun": "Run self-test",
57
+ "selftestRunning": "Testing…",
58
+ "selftestNote": "The self-test only reads browser capabilities; it does not change DSH or affect the Mac.",
59
+ "checkClipboard": "Clipboard write",
60
+ "checkCrypto": "Secure random",
61
+ "checkStorage": "Local storage",
62
+ "checkWebSocket": "WebSocket",
63
+ "checkViewport": "Mobile viewport",
64
+ "checkOnline": "Network status",
65
+ "checkPass": "Pass",
66
+ "checkWarn": "Note",
67
+ "checkFail": "Fail",
68
+ "limitsTitle": "Platform limits (not plugin defects)",
69
+ "limit1": "iOS Safari has no desktop drag-and-drop; use the system file picker.",
70
+ "limit2": "iOS suspends background pages; keep DSH in the foreground for long tasks.",
71
+ "limit3": "Safari hands downloads to the Files app; iOS chooses the location.",
72
+ "zeroImpactTitle": "Zero-impact design for the Mac",
73
+ "zeroImpactBody": "The plugin adds exactly three routes, one sidebar entry, and one main panel. It does not modify core code, auth, config, theme, layout, or other plugins, and it never polls in the background.",
74
+ "mainAria": "Mobile access panel",
75
+ "codeAria": "Pairing code",
76
+ "reload": "Reload",
77
+ "unknown": "Unknown"
78
+ }
79
+ }
package/locale/zh.json ADDED
@@ -0,0 +1,79 @@
1
+ {
2
+ "meta": {
3
+ "title": "手机访问",
4
+ "description": "让 iPhone 与 iPad 通过同一局域网,用 Safari 安全打开并完整使用官方 DeepSeek Harness Web。"
5
+ },
6
+ "ui": {
7
+ "panel": "手机访问",
8
+ "title": "手机访问",
9
+ "subtitle": "在同一局域网内,用 iPhone 或 iPad 打开这台 Mac 上的官方 DSH。",
10
+ "statusLoading": "正在读取配对状态",
11
+ "statusReady": "已就绪",
12
+ "statusLoopback": "仅回环地址",
13
+ "statusLocked": "配对码已锁定",
14
+ "statusExpired": "配对码已过期",
15
+ "statusError": "暂时无法读取状态",
16
+ "errorTitle": "无法读取配对状态",
17
+ "retry": "重新加载",
18
+ "urlLabel": "iPhone 打开的地址",
19
+ "urlHint": "在 iPhone 的 Safari 地址栏输入这个地址。",
20
+ "codeLabel": "配对码",
21
+ "codeHint": "在 iPhone 的配对页输入这 8 位数字。",
22
+ "copyLink": "复制地址",
23
+ "copyCode": "复制配对码",
24
+ "copied": "已复制",
25
+ "copyFailed": "复制失败,请手动选择文本",
26
+ "rotate": "刷新配对码",
27
+ "rotating": "正在刷新…",
28
+ "toastRotated": "配对码已刷新",
29
+ "rotateFailed": "刷新失败,请重试",
30
+ "expiry": "有效至",
31
+ "expiresIn": "剩余",
32
+ "expired": "已过期",
33
+ "locked": "已锁定",
34
+ "never": "尚未配对",
35
+ "lastPaired": "最近配对",
36
+ "pairCount": "成功配对次数",
37
+ "stepsTitle": "在 iPhone 上连接",
38
+ "step1": "确认 iPhone 与 Mac 连接同一个 Wi-Fi,且没有开启会隔离设备的访客网络。",
39
+ "step2": "在 iPhone Safari 打开上面的地址,输入配对码并点「连接 DSH」。",
40
+ "step3": "配对成功后会自动进入 DSH;再点 Safari 分享按钮,选择「添加到主屏幕」。",
41
+ "diagnosticsTitle": "连接诊断",
42
+ "bindLabel": "Mac 监听地址",
43
+ "deviceLabel": "当前设备",
44
+ "deviceIOS": "iPhone / iPad",
45
+ "deviceOther": "电脑或其他设备",
46
+ "launchMode": "启动方式",
47
+ "standaloneYes": "已从主屏幕启动",
48
+ "standaloneNo": "浏览器标签页",
49
+ "secureYes": "安全上下文",
50
+ "secureNo": "HTTP 非安全上下文(剪贴板回退已启用)",
51
+ "loopbackTitle": "iPhone 现在无法连接",
52
+ "loopbackBody": "DSH 只监听了回环地址(localhost / 127.0.0.1)。请把 Host 的 webserver.host 改为这台 Mac 的局域网 IPv4 地址,然后由你手动重启 DSH。",
53
+ "httpTitle": "关于 HTTP 访问",
54
+ "httpBody": "当前是局域网 HTTP。DSH 已为 UUID 和多数复制按钮提供兼容实现;本插件会在 iPhone 上补齐直接调用 navigator.clipboard 的旧代码。",
55
+ "selftestTitle": "浏览器能力自检",
56
+ "selftestRun": "运行自检",
57
+ "selftestRunning": "正在自检…",
58
+ "selftestNote": "自检只读取浏览器能力,不修改 DSH 配置,也不影响 Mac。",
59
+ "checkClipboard": "剪贴板写入",
60
+ "checkCrypto": "安全随机数",
61
+ "checkStorage": "本地存储",
62
+ "checkWebSocket": "WebSocket",
63
+ "checkViewport": "移动端视口",
64
+ "checkOnline": "网络状态",
65
+ "checkPass": "通过",
66
+ "checkWarn": "注意",
67
+ "checkFail": "失败",
68
+ "limitsTitle": "平台边界(不是插件缺陷)",
69
+ "limit1": "iOS Safari 不支持桌面式拖放;文件请用系统选择器上传。",
70
+ "limit2": "iOS 会在后台挂起网页;长任务请保持 DSH 页面在前台。",
71
+ "limit3": "下载文件由 Safari 交给「文件」App,落点由 iOS 决定。",
72
+ "zeroImpactTitle": "对 Mac 的零影响设计",
73
+ "zeroImpactBody": "插件只增加三个精确路由、一个左侧栏入口和一个主面板;不修改核心代码、认证、配置、主题、布局或其他插件,也不在后台轮询。",
74
+ "mainAria": "手机访问面板",
75
+ "codeAria": "配对码",
76
+ "reload": "刷新",
77
+ "unknown": "未知"
78
+ }
79
+ }
package/package.json CHANGED
@@ -1,6 +1,52 @@
1
1
  {
2
2
  "name": "dsh-mobile-access",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.0.0",
4
+ "description": "Independent DSH bundle that pairs iPhones and iPads with the official DeepSeek Harness Web GUI over the local network.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "publishConfig": {
8
+ "access": "public"
9
+ },
10
+ "engines": {
11
+ "node": ">=22"
12
+ },
13
+ "exports": {
14
+ ".": "./index.js",
15
+ "./client": "./client.js",
16
+ "./locale/*.json": "./locale/*.json",
17
+ "./icon": "./icon.svg"
18
+ },
19
+ "files": [
20
+ "index.js",
21
+ "service.js",
22
+ "routes.js",
23
+ "html.js",
24
+ "types.js",
25
+ "client.js",
26
+ "cordis.patch.yml",
27
+ "locale/*.json",
28
+ "icon.svg",
29
+ "README.md",
30
+ "LICENSE"
31
+ ],
32
+ "scripts": {
33
+ "build": "node scripts/build.mjs",
34
+ "typecheck": "node scripts/build.mjs --check",
35
+ "test": "node scripts/build.mjs && node --test tests/*.test.mjs",
36
+ "verify": "bash scripts/verify.sh"
37
+ },
38
+ "dsh": {
39
+ "bundle": {
40
+ "patch": "./cordis.patch.yml"
41
+ },
42
+ "client": {
43
+ "platform": "web",
44
+ "immediately": true,
45
+ "inject": [
46
+ "@deepseek-ai/dsh-client-locale",
47
+ "@deepseek-ai/dsh-client-ui-layout",
48
+ "@deepseek-ai/dsh-client-ui-sidebar"
49
+ ]
50
+ }
51
+ }
52
+ }