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/README.en.md +202 -0
- package/README.md +42 -7
- package/lib/client.js +3 -3
- package/lib/index.js +31 -0
- package/package.json +57 -56
- package/scripts/setup-code-server.mjs +89 -8
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.
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
"
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
"
|
|
52
|
-
|
|
53
|
-
"
|
|
54
|
-
"
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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',
|
|
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
|
-
|
|
113
|
-
|
|
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
|
-
|
|
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
|
+
}
|