claude-opencode-viewer 2.6.7 → 2.6.8
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/package.json +2 -1
- package/server.js +17 -102
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-opencode-viewer",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.8",
|
|
4
4
|
"description": "A unified terminal viewer for Claude Code and OpenCode with seamless switching",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "server.js",
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"better-sqlite3": "^11.8.1",
|
|
37
37
|
"node-pty": "^1.1.0",
|
|
38
|
+
"selfsigned": "^5.5.0",
|
|
38
39
|
"ws": "^8.19.0"
|
|
39
40
|
},
|
|
40
41
|
"engines": {
|
package/server.js
CHANGED
|
@@ -8,7 +8,6 @@ import { networkInterfaces, platform, arch, homedir } from 'node:os';
|
|
|
8
8
|
import { chmodSync, statSync } from 'node:fs';
|
|
9
9
|
import { execSync, execFile } from 'child_process';
|
|
10
10
|
import { promisify } from 'node:util';
|
|
11
|
-
import { generateKeyPairSync, createSign, randomBytes } from 'node:crypto';
|
|
12
11
|
import { WebSocketServer } from 'ws';
|
|
13
12
|
import Database from 'better-sqlite3';
|
|
14
13
|
|
|
@@ -20,8 +19,8 @@ const PORT = parseInt(process.argv[2]) || 7008;
|
|
|
20
19
|
const IS_PC = process.argv.includes('--pc');
|
|
21
20
|
const USE_HTTPS = process.argv.includes('--https');
|
|
22
21
|
|
|
23
|
-
//
|
|
24
|
-
function getOrCreateCert() {
|
|
22
|
+
// 自签名证书生成(使用 selfsigned 库,不依赖 openssl)
|
|
23
|
+
async function getOrCreateCert() {
|
|
25
24
|
const certDir = join(homedir(), '.cov-certs');
|
|
26
25
|
const keyPath = join(certDir, 'key.pem');
|
|
27
26
|
const certPath = join(certDir, 'cert.pem');
|
|
@@ -33,107 +32,23 @@ function getOrCreateCert() {
|
|
|
33
32
|
console.log('🔐 首次使用 HTTPS,生成自签名证书...');
|
|
34
33
|
mkdirSync(certDir, { recursive: true });
|
|
35
34
|
|
|
36
|
-
const {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
const { default: selfsigned } = await import('selfsigned');
|
|
36
|
+
const attrs = [{ name: 'commonName', value: 'cov-self-signed' }];
|
|
37
|
+
const pems = await selfsigned.generate(attrs, {
|
|
38
|
+
days: 365,
|
|
39
|
+
keySize: 2048,
|
|
40
|
+
extensions: [
|
|
41
|
+
{ name: 'subjectAltName', altNames: [
|
|
42
|
+
{ type: 7, ip: '127.0.0.1' },
|
|
43
|
+
{ type: 2, value: 'localhost' },
|
|
44
|
+
]}
|
|
45
|
+
],
|
|
40
46
|
});
|
|
41
47
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const now = new Date();
|
|
45
|
-
const expire = new Date(now.getTime() + 365 * 24 * 3600 * 1000);
|
|
46
|
-
|
|
47
|
-
function encodeLength(len) {
|
|
48
|
-
if (len < 128) return Buffer.from([len]);
|
|
49
|
-
if (len < 256) return Buffer.from([0x81, len]);
|
|
50
|
-
return Buffer.from([0x82, (len >> 8) & 0xff, len & 0xff]);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function derSequence(buffers) {
|
|
54
|
-
const body = Buffer.concat(buffers);
|
|
55
|
-
return Buffer.concat([Buffer.from([0x30]), encodeLength(body.length), body]);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function derSet(buffers) {
|
|
59
|
-
const body = Buffer.concat(buffers);
|
|
60
|
-
return Buffer.concat([Buffer.from([0x31]), encodeLength(body.length), body]);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function derOid(oid) {
|
|
64
|
-
const parts = oid.split('.').map(Number);
|
|
65
|
-
const bytes = [40 * parts[0] + parts[1]];
|
|
66
|
-
for (let i = 2; i < parts.length; i++) {
|
|
67
|
-
let v = parts[i];
|
|
68
|
-
if (v < 128) { bytes.push(v); }
|
|
69
|
-
else {
|
|
70
|
-
const enc = [];
|
|
71
|
-
enc.push(v & 0x7f); v >>= 7;
|
|
72
|
-
while (v > 0) { enc.push(0x80 | (v & 0x7f)); v >>= 7; }
|
|
73
|
-
bytes.push(...enc.reverse());
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
const buf = Buffer.from(bytes);
|
|
77
|
-
return Buffer.concat([Buffer.from([0x06]), encodeLength(buf.length), buf]);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function derUtf8(str) {
|
|
81
|
-
const buf = Buffer.from(str, 'utf8');
|
|
82
|
-
return Buffer.concat([Buffer.from([0x0c]), encodeLength(buf.length), buf]);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function derInt(buf) {
|
|
86
|
-
if (buf[0] & 0x80) buf = Buffer.concat([Buffer.from([0x00]), buf]);
|
|
87
|
-
return Buffer.concat([Buffer.from([0x02]), encodeLength(buf.length), buf]);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function derBitString(buf) {
|
|
91
|
-
const body = Buffer.concat([Buffer.from([0x00]), buf]);
|
|
92
|
-
return Buffer.concat([Buffer.from([0x03]), encodeLength(body.length), body]);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function derTime(date) {
|
|
96
|
-
const s = date.toISOString().replace(/[-:T]/g, '').slice(0, 14) + 'Z';
|
|
97
|
-
const buf = Buffer.from(s, 'ascii');
|
|
98
|
-
return Buffer.concat([Buffer.from([0x17]), encodeLength(buf.length), buf]);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function derExplicit(tag, content) {
|
|
102
|
-
return Buffer.concat([Buffer.from([0xa0 | tag]), encodeLength(content.length), content]);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const issuer = derSequence([derSet([derSequence([derOid('2.5.4.3'), derUtf8('cov-self-signed')])])]);
|
|
106
|
-
const validity = derSequence([derTime(now), derTime(expire)]);
|
|
107
|
-
const spki = Buffer.from(publicKey);
|
|
108
|
-
|
|
109
|
-
const tbs = derSequence([
|
|
110
|
-
derExplicit(0, derInt(Buffer.from([0x02]))), // version v3
|
|
111
|
-
derInt(serial),
|
|
112
|
-
derSequence([derOid('1.2.840.113549.1.1.11'), Buffer.from([0x05, 0x00])]), // sha256WithRSA
|
|
113
|
-
issuer,
|
|
114
|
-
validity,
|
|
115
|
-
issuer, // subject = issuer (self-signed)
|
|
116
|
-
spki,
|
|
117
|
-
]);
|
|
118
|
-
|
|
119
|
-
const sign = createSign('SHA256');
|
|
120
|
-
sign.update(tbs);
|
|
121
|
-
const signature = sign.sign(privateKey);
|
|
122
|
-
|
|
123
|
-
const cert = derSequence([
|
|
124
|
-
tbs,
|
|
125
|
-
derSequence([derOid('1.2.840.113549.1.1.11'), Buffer.from([0x05, 0x00])]),
|
|
126
|
-
derBitString(signature),
|
|
127
|
-
]);
|
|
128
|
-
|
|
129
|
-
const certPem = '-----BEGIN CERTIFICATE-----\n' +
|
|
130
|
-
cert.toString('base64').match(/.{1,64}/g).join('\n') +
|
|
131
|
-
'\n-----END CERTIFICATE-----\n';
|
|
132
|
-
|
|
133
|
-
writeFileSync(keyPath, privateKey);
|
|
134
|
-
writeFileSync(certPath, certPem);
|
|
48
|
+
writeFileSync(keyPath, pems.private);
|
|
49
|
+
writeFileSync(certPath, pems.cert);
|
|
135
50
|
console.log(`📁 证书已保存到 ${certDir}`);
|
|
136
|
-
return { key:
|
|
51
|
+
return { key: pems.private, cert: pems.cert };
|
|
137
52
|
}
|
|
138
53
|
|
|
139
54
|
// OpenCode 数据库路径(支持环境变量覆盖,自动检测 /halo 环境)
|
|
@@ -679,7 +594,7 @@ const requestHandler = async (req, res) => {
|
|
|
679
594
|
};
|
|
680
595
|
|
|
681
596
|
const server = USE_HTTPS
|
|
682
|
-
? createHttpsServer(getOrCreateCert(), requestHandler)
|
|
597
|
+
? createHttpsServer(await getOrCreateCert(), requestHandler)
|
|
683
598
|
: createServer(requestHandler);
|
|
684
599
|
|
|
685
600
|
const wss = new WebSocketServer({ server, path: '/ws' });
|