xshell 1.0.64 → 1.0.66

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/Terminal.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import 'xterm/css/xterm.css';
2
2
  import { Terminal as XTermTerminal } from 'xterm';
3
+ import { FitAddon } from 'xterm-addon-fit';
3
4
  import { Model } from 'react-object-model';
4
5
  import type { Remote } from './net.browser.js';
5
6
  export declare function Terminal({ font }: {
@@ -7,7 +8,9 @@ export declare function Terminal({ font }: {
7
8
  }): import("react/jsx-runtime").JSX.Element;
8
9
  declare class TerminalModel extends Model<TerminalModel> {
9
10
  term: XTermTerminal;
11
+ fit_addon: FitAddon;
10
12
  stdio_id: number;
13
+ fit(): void;
11
14
  subscribe_stdio(remote: Remote): Promise<void>;
12
15
  unsubscribe_stdio(remote: Remote): Promise<void>;
13
16
  }
package/Terminal.js CHANGED
@@ -29,14 +29,18 @@ export function Terminal({ font }) {
29
29
  term.open(rterminal.current);
30
30
  term.loadAddon(new WebglAddon());
31
31
  fit_addon.fit();
32
- terminal.set({ term });
32
+ terminal.set({ term, fit_addon });
33
33
  })();
34
34
  }, []);
35
35
  return _jsx("div", { className: 'term', ref: rterminal });
36
36
  }
37
37
  class TerminalModel extends Model {
38
38
  term;
39
+ fit_addon;
39
40
  stdio_id = 0;
41
+ fit() {
42
+ this.fit_addon?.fit();
43
+ }
40
44
  async subscribe_stdio(remote) {
41
45
  assert(!this.stdio_id);
42
46
  const id = this.stdio_id = genid();
package/git.d.ts ADDED
@@ -0,0 +1,36 @@
1
+ import { type CallOptions } from './process.js';
2
+ export declare class Git {
3
+ static exe: string;
4
+ cwd: string;
5
+ constructor(cwd: string);
6
+ static init(cwd: string): Promise<Git>;
7
+ static clone(repo: string, fpd: string): Promise<void>;
8
+ call(args?: any[], { color, print }?: {
9
+ color?: boolean;
10
+ print?: CallOptions['print'];
11
+ }): Promise<import("./process.js").CallResult<string>>;
12
+ get_branch(): Promise<string>;
13
+ /** - print: `true` */
14
+ get_branches(print?: boolean): Promise<string[]>;
15
+ has_branch(branch: string): Promise<boolean>;
16
+ fetch(print?: boolean): Promise<void>;
17
+ /** - branch?: `'main'` */
18
+ checkout(branch?: 'main'): Promise<void>;
19
+ checkout(branch?: string): Promise<void>;
20
+ _checkout(branch?: string): Promise<void>;
21
+ merge(branch: string, fast_forward?: boolean): Promise<void>;
22
+ /** 记住当前分支名
23
+ checkout 到 <目标分支>
24
+ merge 当前分支 */
25
+ merge_into(target_branch?: string): Promise<void>;
26
+ /** 1. fetch 远程修改
27
+ 2. checkout `<branch>` 分支
28
+ 3. merge `<remote>/<branch>`
29
+
30
+ 执行前需要保证工作目录 clean
31
+
32
+ - branch?: `'main'` */
33
+ update(branch?: 'main'): Promise<void>;
34
+ update(branch: string): Promise<void>;
35
+ update(branch: string, remote?: string): Promise<void>;
36
+ }
package/git.js ADDED
@@ -0,0 +1,136 @@
1
+ import { assert } from './utils.js';
2
+ import { call } from './process.js';
3
+ import { fread, fmkdir } from './file.js';
4
+ export class Git {
5
+ static exe = 'git';
6
+ cwd;
7
+ constructor(cwd) {
8
+ this.cwd = cwd;
9
+ }
10
+ static async init(cwd) {
11
+ const git = new Git(cwd);
12
+ await git.call(['init']);
13
+ return git;
14
+ }
15
+ static async clone(repo, fpd) {
16
+ await fmkdir(fpd);
17
+ await new this(fpd).call(['clone', repo, '.']);
18
+ }
19
+ async call(args = [], { color = true, print } = {}) {
20
+ return call(Git.exe, [
21
+ ...color ? [] : [
22
+ '-c', 'color.status=false',
23
+ '-c', 'color.ui=false',
24
+ '-c', 'color.branch=false',
25
+ ],
26
+ ...args,
27
+ ], {
28
+ cwd: this.cwd,
29
+ print,
30
+ });
31
+ }
32
+ async get_branch() {
33
+ const head = (await fread(`${this.cwd}.git/HEAD`, { print: false })).trim();
34
+ const branch = head.find('ref: refs/heads/{branch}').branch || head;
35
+ if (!branch)
36
+ throw new Error(`${this.cwd} 仓库 Git.get_branch 无法获取到 branch`);
37
+ return branch;
38
+ // 需要 100+ ms ,性能太差
39
+ // this.branch = (await this.git('branch --show-current')).stdout.trim().strip_ansi()
40
+ }
41
+ /** - print: `true` */
42
+ async get_branches(print = true) {
43
+ return (await this.call(['branch', '-a'], { print })).stdout
44
+ .split_lines()
45
+ .map(l => l.strip_ansi())
46
+ .filter(branch => !branch.includes('origin/HEAD'))
47
+ .map(branch => branch.rm(/^\* /))
48
+ .trim_lines();
49
+ }
50
+ async has_branch(branch) {
51
+ return (await this.get_branches(false))
52
+ .includes(branch);
53
+ }
54
+ async fetch(print = true) {
55
+ if (print)
56
+ console.log('更新远程分支');
57
+ await this.call(['fetch', '--all', '--prune'], { print: { code: false, command: false, stdout: true, stderr: true } });
58
+ }
59
+ async checkout(branch = 'main') {
60
+ if (branch === 'main')
61
+ await this._checkout('main');
62
+ else {
63
+ const branches = await this.get_branches(false);
64
+ if (branches.includes(branch) || branches.find(b => b.endsWith(`/${branch}`) && b.startsWith('remotes/')))
65
+ await this._checkout(branch);
66
+ else {
67
+ console.log(`创建分支 ${branch}`);
68
+ await this.call(['checkout', '-b', branch], { print: { command: false, code: false, stdout: true, stderr: true } });
69
+ }
70
+ }
71
+ }
72
+ async _checkout(branch) {
73
+ try {
74
+ const { stdout, stderr } = await this.call(['checkout', branch], { print: false });
75
+ if (stdout)
76
+ console.log(stdout.trimEnd());
77
+ console.log(/Already on '.*'\n/.test(stderr) ?
78
+ `已经在 ${branch} 分支了`
79
+ : /Switched to branch '.*'/.test(stderr) ?
80
+ `已切换到 ${branch} 分支`
81
+ :
82
+ stderr.trimEnd());
83
+ }
84
+ catch (error) {
85
+ if (error.result) {
86
+ const { stdout, stderr } = error.result;
87
+ if (stdout)
88
+ console.log(stdout);
89
+ if (stderr)
90
+ console.log(stderr);
91
+ }
92
+ throw error;
93
+ }
94
+ }
95
+ async merge(branch, fast_forward = true) {
96
+ console.log(`合并 ${branch} 分支`);
97
+ try {
98
+ const { stdout, stderr } = await this.call([
99
+ 'merge',
100
+ branch,
101
+ ...fast_forward ? [] : ['--no-ff']
102
+ ], { print: false });
103
+ if (stdout === 'Already up to date.\n')
104
+ console.log('当前分支无需更新');
105
+ else
106
+ console.log(stdout.trimEnd());
107
+ if (stderr)
108
+ console.log(stderr);
109
+ }
110
+ catch (error) {
111
+ if (error.result) {
112
+ const { stdout, stderr } = error.result;
113
+ if (stdout)
114
+ console.log(stdout);
115
+ if (stderr)
116
+ console.log(stderr);
117
+ }
118
+ throw error;
119
+ }
120
+ }
121
+ /** 记住当前分支名
122
+ checkout 到 <目标分支>
123
+ merge 当前分支 */
124
+ async merge_into(target_branch = 'main') {
125
+ const branch = await this.get_branch();
126
+ assert(branch !== target_branch);
127
+ await this.checkout(target_branch);
128
+ await this.merge(branch, true);
129
+ }
130
+ async update(branch = 'main', remote = 'origin') {
131
+ await this.fetch();
132
+ await this.checkout(branch);
133
+ await this.merge(`${remote}/${branch}`);
134
+ }
135
+ }
136
+ //# sourceMappingURL=git.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xshell",
3
- "version": "1.0.64",
3
+ "version": "1.0.66",
4
4
  "type": "module",
5
5
  "main": "./index.js",
6
6
  "bin": {
package/server.d.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  /// <reference types="node" resolution-mode="require"/>
2
2
  /// <reference types="node" resolution-mode="require"/>
3
3
  /// <reference types="node" resolution-mode="require"/>
4
- import { type Server as HttpServer, type IncomingHttpHeaders } from 'http';
4
+ /// <reference types="node" resolution-mode="require"/>
5
+ import { type Server as HttpServer, type IncomingHttpHeaders, type IncomingMessage } from 'http';
5
6
  import { type Http2SecureServer, type IncomingHttpHeaders as IncomingHttp2Headers } from 'http2';
7
+ import { type Duplex } from 'stream';
6
8
  import type { WebSocketServer } from 'ws';
7
9
  import type { default as Koa, Context, Next } from 'koa';
8
10
  declare module 'koa' {
@@ -70,6 +72,7 @@ export declare class Server {
70
72
  on_error(error: Error & {
71
73
  code?: string;
72
74
  }, ctx?: Context): void;
75
+ on_upgrade(request: IncomingMessage, socket: Duplex, head: Buffer): void;
73
76
  /** 可被子类重写添加额外的中间件,在 start 时被调用 */
74
77
  init_app(app: Koa): Promise<void>;
75
78
  entry(ctx: Context, next: Next): Promise<void>;
package/server.js CHANGED
@@ -1,4 +1,4 @@
1
- import { createServer as http_create_server } from 'http';
1
+ import { createServer as http_create_server, } from 'http';
2
2
  import { createSecureServer as http2_create_server } from 'http2';
3
3
  import { createSecureContext } from 'tls';
4
4
  import zlib from 'zlib';
@@ -128,37 +128,9 @@ export class Server {
128
128
  this.remote.handle(new Uint8Array(data), ws);
129
129
  });
130
130
  });
131
- this.http_server.on('upgrade', (request, socket, head) => {
132
- // url 只有路径部分
133
- const { url, headers, headers: { host = '' }, } = request;
134
- const ip = request.socket.remoteAddress.replace(/^::ffff:/, '');
135
- console.log(
136
- // 时间
137
- `${new Date().to_time_str()} ` +
138
- // ip(位置)
139
- (ip || '').limit(60) + ' ' +
140
- // ua
141
- this.format_ua(headers).limit(56) + ' ' +
142
- // https/2.0
143
- `${this.colors ? 'websocket'.limit(10).magenta : 'websocket'.limit(10)} ` +
144
- // method
145
- ''.limit(6) + ' ' +
146
- // host
147
- `${host.limit(24)} ` +
148
- // path
149
- (this.colors ? url.yellow : url));
150
- switch (url) {
151
- case '/':
152
- this.websocket_server.handleUpgrade(request, socket, head, ws => {
153
- ws.binaryType = 'arraybuffer';
154
- this.websocket_server.emit('connection', ws, request);
155
- });
156
- return;
157
- default:
158
- console.log(`${' '.repeat(13)} connect 404: ${url}`.red);
159
- socket.destroy();
160
- }
161
- });
131
+ const on_upgrade = this.on_upgrade.bind(this);
132
+ this.http_server.on('upgrade', on_upgrade);
133
+ this.http2_server?.on('upgrade', on_upgrade);
162
134
  // 将输出到 stdout, stderr 的内容 copy 一份通过 websocket 发到 web shell
163
135
  if (this.stdio_subscribable) {
164
136
  this.remote.funcs = {
@@ -246,6 +218,37 @@ export class Server {
246
218
  console.log(ctx);
247
219
  }
248
220
  }
221
+ on_upgrade(request, socket, head) {
222
+ // url 只有路径部分
223
+ const { url, headers, headers: { host = '' }, } = request;
224
+ const ip = request.socket.remoteAddress.replace(/^::ffff:/, '');
225
+ console.log(
226
+ // 时间
227
+ `${new Date().to_time_str()} ` +
228
+ // ip(位置)
229
+ (ip || '').limit(60) + ' ' +
230
+ // ua
231
+ this.format_ua(headers).limit(56) + ' ' +
232
+ // https/2.0
233
+ `${this.colors ? 'websocket'.limit(10).magenta : 'websocket'.limit(10)} ` +
234
+ // method
235
+ ''.limit(6) + ' ' +
236
+ // host
237
+ `${host.limit(24)} ` +
238
+ // path
239
+ (this.colors ? url.yellow : url));
240
+ switch (url) {
241
+ case '/':
242
+ this.websocket_server.handleUpgrade(request, socket, head, ws => {
243
+ ws.binaryType = 'arraybuffer';
244
+ this.websocket_server.emit('connection', ws, request);
245
+ });
246
+ return;
247
+ default:
248
+ console.log(`${' '.repeat(13)} connect 404: ${url}`.red);
249
+ socket.destroy();
250
+ }
251
+ }
249
252
  /** 可被子类重写添加额外的中间件,在 start 时被调用 */
250
253
  async init_app(app) {
251
254
  }