dsh-code-server-app 0.1.20 → 0.1.24

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/lib/index.js CHANGED
@@ -66,6 +66,9 @@ export const Config = z.object({
66
66
  /** windowedOpen=false(默认)点击悬浮球打开内部浮动窗口;
67
67
  * true 时改为在浏览器新标签页打开 code-server(自动启动并跟随工作区)。 */
68
68
  windowedOpen: z.boolean().default(false),
69
+ /** installGuideDismissed=false(默认)环境未就绪时弹出安装指引;
70
+ * true = 用户已永久关闭安装指引(不再弹出;设置卡可重新打开)。 */
71
+ installGuideDismissed: z.boolean().default(false),
69
72
  });
70
73
 
71
74
  const DEFAULT_CONFIG = {
@@ -281,6 +284,7 @@ export async function apply(ctx, config) {
281
284
  const settingsSvc = ctx.get('settings');
282
285
  let reserveComposer = true;
283
286
  let windowedOpen = false;
287
+ let installGuideDismissed = false;
284
288
  if (settingsSvc !== undefined && typeof settingsSvc.register === 'function') {
285
289
  try {
286
290
  const scope = settingsSvc.register(SETTINGS_NS, Config);
@@ -289,6 +293,7 @@ export async function apply(ctx, config) {
289
293
  const resolved = scope.get();
290
294
  reserveComposer = resolved && typeof resolved.reserveComposer === 'boolean' ? resolved.reserveComposer : true;
291
295
  windowedOpen = resolved && typeof resolved.windowedOpen === 'boolean' ? resolved.windowedOpen : false;
296
+ installGuideDismissed = resolved && typeof resolved.installGuideDismissed === 'boolean' ? resolved.installGuideDismissed : false;
292
297
  }
293
298
  scope.watch((next) => {
294
299
  if (next != null && typeof next.reserveComposer === 'boolean') {
@@ -299,6 +304,10 @@ export async function apply(ctx, config) {
299
304
  windowedOpen = next.windowedOpen;
300
305
  console.log(`[code-server] windowedOpen updated: ${windowedOpen}`);
301
306
  }
307
+ if (next != null && typeof next.installGuideDismissed === 'boolean') {
308
+ installGuideDismissed = next.installGuideDismissed;
309
+ console.log(`[code-server] installGuideDismissed updated: ${installGuideDismissed}`);
310
+ }
302
311
  });
303
312
  } catch (error) {
304
313
  console.error(`[code-server] settings unavailable; using defaults (reserveComposer=true, windowedOpen=false): ${error.message}`);
@@ -340,6 +349,7 @@ export async function apply(ctx, config) {
340
349
  adopted: state.adopted,
341
350
  reserveComposer,
342
351
  windowedOpen,
352
+ installGuideDismissed,
343
353
  env: state.env,
344
354
  setup: {
345
355
  running: state.setup.running,
@@ -347,9 +357,30 @@ export async function apply(ctx, config) {
347
357
  ok: state.setup.ok,
348
358
  logTail: state.setup.logTail.slice(-LOG_TAIL_MAX),
349
359
  },
360
+ lastSetupError: readLastSetupError(),
350
361
  };
351
362
  }
352
363
 
364
+ /** 读 setup 失败标记文件(postinstall 失败时写入;成功则不存在)→ 用户可查看。
365
+ * 标记在安装根:<profile>\.code-server-app\last-setup-error.json。 */
366
+ function readLastSetupError() {
367
+ try {
368
+ const here = path.dirname(fileURLToPath(import.meta.url));
369
+ // 插件目录 <profile>\node_modules\<name> 向上两级 = <profile> 根
370
+ const profileRoot = path.join(here, '..', '..');
371
+ const marker = path.join(profileRoot, '.code-server-app', 'last-setup-error.json');
372
+ if (!fs.existsSync(marker)) return null;
373
+ const raw = fs.readFileSync(marker, 'utf8');
374
+ const j = JSON.parse(raw);
375
+ return {
376
+ at: j && typeof j.at === 'string' ? j.at : null,
377
+ error: j && typeof j.error === 'string' ? j.error : '安装脚本失败(无详情)',
378
+ };
379
+ } catch {
380
+ return null;
381
+ }
382
+ }
383
+
353
384
  function appendLog(chunk) {
354
385
  try {
355
386
  const text = typeof chunk === 'string' ? chunk : chunk.toString('utf8');
package/package.json CHANGED
@@ -1,56 +1,57 @@
1
- {
2
- "name": "dsh-code-server-app",
3
- "version": "0.1.20",
4
- "description": "Integrate code-server (VS Code in the browser) into DSH Web: floating ball entry + fullscreen overlay, host-side process lifecycle over a /code-server JSON API (motion-based spring window animations).",
5
- "homepage": "https://github.com/jinsiyu/dsh-code-server-app",
6
- "repository": {
7
- "type": "git",
8
- "url": "git+https://github.com/jinsiyu/dsh-code-server-app.git"
9
- },
10
- "type": "module",
11
- "main": "lib/index.js",
12
- "exports": {
13
- ".": "./lib/index.js",
14
- "./client": "./lib/client.js",
15
- "./package.json": "./package.json"
16
- },
17
- "dsh": {
18
- "bundle": {
19
- "patch": "./cordis.patch.yml"
20
- },
21
- "client": {
22
- "platform": "web",
23
- "inject": [
24
- "@deepseek-ai/dsh-client-connection",
25
- "@deepseek-ai/dsh-client-runtime",
26
- "@deepseek-ai/dsh-client-ui-settings"
27
- ]
28
- }
29
- },
30
- "files": [
31
- "lib/index.js",
32
- "lib/client.js",
33
- "assets/favicon.ico",
34
- "assets/favicon.svg",
35
- "assets/extensions/dshcs-open-file",
36
- "scripts/setup-code-server.mjs",
37
- "cordis.patch.yml",
38
- "LICENSE",
39
- "README.md"
40
- ],
41
- "scripts": {
42
- "postinstall": "node scripts/setup-code-server.mjs",
43
- "setup:code-server": "node scripts/setup-code-server.mjs",
44
- "build:client": "node scripts/build-client.mjs"
45
- },
46
- "devDependencies": {
47
- "esbuild": "^0.25.0",
48
- "motion": "^12.0.0"
49
- },
50
- "license": "MIT",
51
- "allowScripts": {
52
- "code-server": false,
53
- "argon2@0.44.0": true,
54
- "unrs-resolver@1.11.1": true
55
- }
56
- }
1
+ {
2
+ "name": "dsh-code-server-app",
3
+ "version": "0.1.24",
4
+ "description": "Integrate code-server (VS Code in the browser) into DSH Web: floating ball entry + fullscreen overlay, host-side process lifecycle over a /code-server JSON API (motion-based spring window animations).",
5
+ "homepage": "https://github.com/jinsiyu/dsh-code-server-app",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/jinsiyu/dsh-code-server-app.git"
9
+ },
10
+ "type": "module",
11
+ "main": "lib/index.js",
12
+ "exports": {
13
+ ".": "./lib/index.js",
14
+ "./client": "./lib/client.js",
15
+ "./package.json": "./package.json"
16
+ },
17
+ "dsh": {
18
+ "bundle": {
19
+ "patch": "./cordis.patch.yml"
20
+ },
21
+ "client": {
22
+ "platform": "web",
23
+ "inject": [
24
+ "@deepseek-ai/dsh-client-connection",
25
+ "@deepseek-ai/dsh-client-runtime",
26
+ "@deepseek-ai/dsh-client-ui-settings"
27
+ ]
28
+ }
29
+ },
30
+ "files": [
31
+ "lib/index.js",
32
+ "lib/client.js",
33
+ "assets/favicon.ico",
34
+ "assets/favicon.svg",
35
+ "assets/extensions/dshcs-open-file",
36
+ "scripts/setup-code-server.mjs",
37
+ "cordis.patch.yml",
38
+ "LICENSE",
39
+ "README.md",
40
+ "README.en.md"
41
+ ],
42
+ "scripts": {
43
+ "postinstall": "node scripts/setup-code-server.mjs",
44
+ "setup:code-server": "node scripts/setup-code-server.mjs",
45
+ "build:client": "node scripts/build-client.mjs"
46
+ },
47
+ "devDependencies": {
48
+ "esbuild": "^0.25.0",
49
+ "motion": "^12.0.0"
50
+ },
51
+ "license": "MIT",
52
+ "allowScripts": {
53
+ "code-server": false,
54
+ "argon2": true,
55
+ "unrs-resolver": true
56
+ }
57
+ }
@@ -11,14 +11,15 @@
11
11
  // 幂等:code-server 已实例化(entry + VS Code 内部依赖 + argon2 native 均在)则跳过;
12
12
  // pnpm 重装插件后会重跑 postinstall → 自动重新自装(自愈)。
13
13
  import { spawnSync } from 'node:child_process';
14
- import { existsSync, mkdirSync, writeFileSync, copyFileSync, readdirSync } from 'node:fs';
14
+ import { existsSync, mkdirSync, writeFileSync, readFileSync, copyFileSync, readdirSync, rmSync } from 'node:fs';
15
15
  import { dirname, join } from 'node:path';
16
16
  import { fileURLToPath } from 'node:url';
17
17
 
18
18
  const here = dirname(fileURLToPath(import.meta.url)); // .../dsh-code-server/scripts
19
19
  const pkgRoot = join(here, '..'); // <profile>\node_modules\dsh-code-server
20
20
 
21
- const CODE_SERVER_VERSION = '4.134.0';
21
+ // 不锁定 code-server 版本:安装时总是取 npm 最新版(latest)。
22
+ // 可用环境变量 DSHCS_CODE_SERVER_VERSION 显式锁版本(如 "4.134.0"),缺省为空 = latest。
22
23
 
23
24
  function run(cmd, args, cwd) {
24
25
  const useShell = process.platform === 'win32';
@@ -30,6 +31,43 @@ function npm(cmdArgs, cwd) {
30
31
  run(process.platform === 'win32' ? 'npm.cmd' : 'npm', cmdArgs, cwd);
31
32
  }
32
33
 
34
+ /** 查询 npm 最新 code-server 版本;失败(离线/网络/权限)返回 null。
35
+ * 优先直连 registry JSON API(子进程 Node ESM,无 npm 缓存写);失败再试 npm view。 */
36
+ function latestCodeServerVersion() {
37
+ try {
38
+ const code = "const r=await fetch('https://registry.npmjs.org/code-server/latest');const j=await r.json();process.stdout.write(j&&j.version||'')";
39
+ const res = spawnSync(process.execPath, ['--input-type=module', '-e', code], { encoding: 'utf8' });
40
+ const v = String(res.stdout || '').trim();
41
+ if (v !== '' && /^\d+\./.test(v)) return v;
42
+ } catch { /* fall through */ }
43
+ try {
44
+ const npmBin = process.platform === 'win32' ? 'npm.cmd' : 'npm';
45
+ const res = spawnSync(npmBin, ['view', 'code-server', 'version'], { encoding: 'utf8', shell: process.platform === 'win32' });
46
+ if (res.status === 0) {
47
+ const v = String(res.stdout || '').trim();
48
+ if (v !== '' && /^\d+\./.test(v)) return v;
49
+ }
50
+ } catch { /* fall through */ }
51
+ return null;
52
+ }
53
+
54
+ /** 待安装的版本:DSHCS_CODE_SERVER_VERSION 优先(显式锁定),否则 latest 查询,再否则 latest 安装。 */
55
+ function desiredVersion() {
56
+ const locked = process.env.DSHCS_CODE_SERVER_VERSION;
57
+ if (typeof locked === 'string' && locked.trim() !== '') return locked.trim();
58
+ return latestCodeServerVersion(); // null → npm install code-server(不带版本)=latest
59
+ }
60
+
61
+ /** 已安装版本(读 cs/package.json);读取失败返回 null。 */
62
+ function installedVersion(cs) {
63
+ try {
64
+ const pkg = JSON.parse(readFileSync(join(cs, 'package.json'), 'utf8'));
65
+ return typeof pkg.version === 'string' ? pkg.version : null;
66
+ } catch {
67
+ return null;
68
+ }
69
+ }
70
+
33
71
  function ensureNpmDeps(dir) {
34
72
  if (!existsSync(join(dir, 'package.json'))) return;
35
73
  // --omit=dev:只装生产依赖(native 模块在依赖 postinstall 内建)
@@ -83,10 +121,14 @@ function installPrefix() {
83
121
 
84
122
  function installCodeServer() {
85
123
  const prefix = installPrefix();
86
- console.log(`[setup-code-server] npm 自装 code-server@${CODE_SERVER_VERSION}(target: ${prefix} → node_modules/code-server)${
124
+ const want = desiredVersion();
125
+ const versionSpec = typeof want === 'string' && want !== '' ? `code-server@${want}` : 'code-server';
126
+ console.log(`[setup-code-server] npm 自装 ${versionSpec}(latest)${
127
+ want != null ? '' : ''}(target: ${prefix} → node_modules/code-server)${
87
128
  prefix === pkgRoot ? '(包内回退)' : '(profile 专用目录)'}…`);
88
129
  // npm 读取 prefix 项目自己的 package.json 的 allowScripts;在安装根写一个最小配置,
89
130
  // 让 code-server:false(跳过官方 sh postinstall)、argon2/unrs:true(native 构建)生效。
131
+ // allowScripts 不带版本号(任何 argon2/unrs 版本都构建 native,避免版本变化后失配)。
90
132
  const appPkg = join(prefix, 'package.json');
91
133
  if (!existsSync(appPkg)) {
92
134
  try {
@@ -95,22 +137,30 @@ function installCodeServer() {
95
137
  name: 'dsh-code-server-app',
96
138
  private: true,
97
139
  version: '0.0.0',
98
- allowScripts: { 'code-server': false, 'argon2@0.44.0': true, 'unrs-resolver@1.11.1': true },
140
+ allowScripts: { 'code-server': false, 'argon2': true, 'unrs-resolver': true },
99
141
  }, null, 2), 'utf8');
100
142
  } catch (e) {
101
143
  console.warn('[setup-code-server] 安装根 package.json 写入失败(allowScripts 可能不生效):', e.message);
102
144
  }
103
145
  }
104
146
  npm([
105
- 'install', `code-server@${CODE_SERVER_VERSION}`,
147
+ 'install', versionSpec,
106
148
  '--no-save', '--no-audit', '--no-fund', '--prefix', prefix,
107
149
  ], prefix);
108
150
  }
109
151
 
110
152
  function main() {
111
153
  let cs = findCodeServer();
112
- if (cs !== null && isComplete(cs)) {
113
- console.log('[setup-code-server] code-server 已实例化(entry + 内部依赖 + native 均在),跳过');
154
+ const want = desiredVersion();
155
+ const have = cs !== null ? installedVersion(cs) : null;
156
+ // 升级判定独立于 isComplete:已有实例且(有 latest 且版本不同)→ 重装到最新。
157
+ if (want != null && have !== null && have !== want) {
158
+ console.log(`[setup-code-server] 当前 code-server@${have},最新 ${want}——自动升级…`);
159
+ installCodeServer();
160
+ cs = findCodeServer();
161
+ } else if (cs !== null && isComplete(cs)) {
162
+ console.log(`[setup-code-server] code-server 已实例化(entry + 内部依赖 + native 均在),跳过${
163
+ have !== null ? `(版本 ${have})` : ''}` + (want != null ? `;latest: ${want}` : ''));
114
164
  return;
115
165
  }
116
166
  if (cs === null || !existsSync(join(cs, 'out', 'node', 'entry.js'))) {
@@ -147,9 +197,40 @@ function main() {
147
197
  console.log('[setup-code-server] 完成;entry:', existsSync(join(cs, 'out', 'node', 'entry.js')));
148
198
  }
149
199
 
200
+ /** 错误标记文件:安装失败时写入(host status 读取 → client 弹窗给用户查看),
201
+ * 成功安装后删除。位置 = 安装根(与安装产物同目录,独立于 pnpm 日志)。 */
202
+ function errorMarkerPath() {
203
+ return join(installPrefix(), 'last-setup-error.json');
204
+ }
205
+
206
+ function writeErrorMarker(err) {
207
+ try {
208
+ const p = errorMarkerPath();
209
+ mkdirSync(dirname(p), { recursive: true });
210
+ writeFileSync(p, JSON.stringify({
211
+ at: new Date().toISOString(),
212
+ error: err && err.message ? err.message : String(err),
213
+ }, null, 2), 'utf8');
214
+ } catch { /* 标记失败不影响主流程 */ }
215
+ }
216
+
217
+ function clearErrorMarker() {
218
+ try {
219
+ const p = errorMarkerPath();
220
+ if (existsSync(p)) rmSync(p, { force: true });
221
+ } catch { /* ignore */ }
222
+ }
223
+
150
224
  try {
151
225
  main();
226
+ clearErrorMarker();
152
227
  } catch (e) {
153
- console.error('[setup-code-server] 安装失败(插件仍可启动;README 有恢复指引):', e.message);
228
+ const msg = errText(e);
229
+ console.error('[setup-code-server] 安装失败(插件仍可启动;README 有恢复指引):', msg);
230
+ writeErrorMarker(e);
154
231
  process.exitCode = 0; // 不阻断 pnpm/npm install
155
232
  }
233
+
234
+ function errText(e) {
235
+ return e && e.message ? e.message : String(e);
236
+ }