glad-web 1.0.46 → 2.0.1

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.
Files changed (74) hide show
  1. package/README.md +4 -192
  2. package/THIRD_PARTY_NOTICES.md +27 -0
  3. package/bin/glad.cjs +56 -0
  4. package/package.json +19 -61
  5. package/README.zh-CN.md +0 -198
  6. package/assets/logo.svg +0 -43
  7. package/bin/cli.js +0 -65
  8. package/lib/ai-tools/demo/enhanced-demo.js +0 -625
  9. package/lib/ai-tools/demo/index.js +0 -24
  10. package/lib/ai-tools/demo/responses.js +0 -88
  11. package/lib/ai-tools/detector.js +0 -76
  12. package/lib/ai-tools/registry.js +0 -300
  13. package/lib/claude/cli-usage.js +0 -95
  14. package/lib/claude/config.js +0 -82
  15. package/lib/claude/structured-session.js +0 -884
  16. package/lib/claude/transcript-repository.js +0 -216
  17. package/lib/codex/image-store.js +0 -174
  18. package/lib/codex/structured-session.js +0 -1590
  19. package/lib/commands/config.js +0 -78
  20. package/lib/commands/tools.js +0 -128
  21. package/lib/commands/web.js +0 -605
  22. package/lib/config/constants.js +0 -17
  23. package/lib/config/manager.js +0 -108
  24. package/lib/git/service.js +0 -83
  25. package/lib/notifications/message-formatter.js +0 -94
  26. package/lib/notifications/notification-service.js +0 -143
  27. package/lib/notifications/serverchan-client.js +0 -58
  28. package/lib/notifications/serverchan-settings-store.js +0 -115
  29. package/lib/schedule/job-runner.js +0 -162
  30. package/lib/schedule/job-store.js +0 -167
  31. package/lib/schedule/key-sequences.js +0 -49
  32. package/lib/schedule/scheduler-service.js +0 -39
  33. package/lib/server/routes/notifications.js +0 -52
  34. package/lib/server/routes/providers.js +0 -114
  35. package/lib/server/routes/schedules.js +0 -54
  36. package/lib/server/routes/skillhub.js +0 -104
  37. package/lib/server/routes/usage.js +0 -23
  38. package/lib/server/routes/workspace.js +0 -77
  39. package/lib/session/buffer.js +0 -102
  40. package/lib/session/file-attachment-store.js +0 -168
  41. package/lib/session/pty-manager.js +0 -255
  42. package/lib/session/rendered-history.js +0 -225
  43. package/lib/session/session-manager.js +0 -1032
  44. package/lib/session/text-history.js +0 -274
  45. package/lib/skillhub/client.js +0 -121
  46. package/lib/skillhub/settings-store.js +0 -168
  47. package/lib/skillhub/skill-installer.js +0 -320
  48. package/lib/usage/ccusage-runner.js +0 -128
  49. package/lib/usage/source-catalog.js +0 -26
  50. package/lib/usage/usage-service.js +0 -226
  51. package/lib/utils/logger.js +0 -74
  52. package/lib/utils/pid.js +0 -67
  53. package/lib/utils/validation.js +0 -53
  54. package/lib/web/bootstrap.js +0 -34
  55. package/lib/web/claude.js +0 -1150
  56. package/lib/web/codex.js +0 -1045
  57. package/lib/web/composer.js +0 -493
  58. package/lib/web/core.js +0 -385
  59. package/lib/web/git.js +0 -535
  60. package/lib/web/gitgraph.js +0 -293
  61. package/lib/web/index.html +0 -547
  62. package/lib/web/layout.js +0 -69
  63. package/lib/web/notifications.js +0 -164
  64. package/lib/web/schedules.js +0 -245
  65. package/lib/web/session.js +0 -361
  66. package/lib/web/shell.js +0 -74
  67. package/lib/web/skillhub.js +0 -197
  68. package/lib/web/styles.css +0 -932
  69. package/lib/web/terminal-scroll.js +0 -81
  70. package/lib/web/theme.js +0 -60
  71. package/lib/web/timed-inputs.js +0 -216
  72. package/lib/web/usage.js +0 -323
  73. package/lib/workspace/service.js +0 -77
  74. package/scripts/check-syntax.js +0 -26
@@ -1,255 +0,0 @@
1
- const pty = require('node-pty');
2
- const os = require('os');
3
- const crypto = require('crypto');
4
- const logger = require('../utils/logger');
5
-
6
- function previewText(text, maxChars = 500) {
7
- const normalized = String(text || '')
8
- .replace(/\r/g, '\\r')
9
- .replace(/\n/g, '\\n')
10
- .replace(/\t/g, '\\t')
11
- .replace(/\x1b/g, '\\x1b');
12
- return normalized.length > maxChars ? normalized.slice(0, maxChars) + '...' : normalized;
13
- }
14
-
15
- class PTYManager {
16
- constructor(tool, workingDir, buffer, options = {}) {
17
- this.tool = tool;
18
- this.workingDir = workingDir;
19
- this.buffer = buffer;
20
- this.silent = options.silent || false;
21
- this.ptyProcess = null;
22
- this.onDataCallback = null;
23
- this.onExitCallback = null;
24
- this.localResizeListener = null;
25
-
26
- this.tuiTools = ['antigravity', 'opencode', 'kilo'];
27
- this.isTUIMode = this.tuiTools.includes(tool.key);
28
-
29
- this.recentOutputs = [];
30
- this.duplicateThresholdMs = 150;
31
- this.maxRecentOutputs = 10;
32
- this.debugOutputCount = 0;
33
- }
34
-
35
- // Start PTY process
36
- start(additionalArgs = []) {
37
- const isWindows = os.platform() === 'win32';
38
- const args = [...this.tool.args, ...additionalArgs];
39
-
40
- logger.info(`Starting ${this.tool.displayName}...`);
41
-
42
- try {
43
- if (!this.silent) {
44
- process.stdout.write('\x1b[?2004l');
45
- }
46
-
47
- let spawnCommand = this.tool.command;
48
- let spawnArgs = args;
49
-
50
- if (isWindows) {
51
- spawnCommand = 'cmd.exe';
52
- spawnArgs = ['/c', this.tool.command, ...args];
53
- } else {
54
- spawnCommand = 'bash';
55
- const escaped = [this.tool.command, ...args]
56
- .map(a => `'${String(a).replace(/'/g, "'\\''")}'`)
57
- .join(' ');
58
- spawnArgs = ['-i', '-c', escaped];
59
- }
60
-
61
- let spawnEnv = {
62
- ...process.env,
63
- TERM: 'xterm-256color',
64
- COLORTERM: 'truecolor',
65
- FORCE_COLOR: '1',
66
- CLICOLOR: '1'
67
- };
68
- delete spawnEnv.NO_COLOR;
69
- delete spawnEnv.STY;
70
- delete spawnEnv.WINDOW;
71
- delete spawnEnv.TERMCAP;
72
-
73
- if (isWindows) {
74
- const path = require('path');
75
- const npmGlobalBin = path.join(process.env.APPDATA || '', 'npm');
76
- const currentPath = process.env.PATH || '';
77
-
78
- spawnEnv = {
79
- ...spawnEnv,
80
- PATH: `${npmGlobalBin};${currentPath}`
81
- };
82
- }
83
-
84
- logger.debugInfo(`[pty-start] tool=${this.tool.key}; displayName=${this.tool.displayName}; command=${spawnCommand}; args=${JSON.stringify(spawnArgs)}; cwd=${this.workingDir}`);
85
- logger.debugInfo(`[pty-start] PATH=${spawnEnv.PATH || ''}; SHELL=${spawnEnv.SHELL || ''}; TERM=${spawnEnv.TERM || ''}; HOME=${spawnEnv.HOME || ''}`);
86
-
87
- this.ptyProcess = pty.spawn(spawnCommand, spawnArgs, {
88
- name: 'xterm-256color',
89
- cols: this.silent ? 80 : (process.stdout.columns || 80),
90
- rows: this.silent ? 24 : (process.stdout.rows || 24),
91
- cwd: this.workingDir,
92
- env: spawnEnv
93
- });
94
-
95
- logger.success(`${this.tool.displayName} started (PID: ${this.ptyProcess.pid})`);
96
-
97
- this.ptyProcess.onData((data) => {
98
- this.handlePTYOutput(data);
99
- });
100
-
101
- this.ptyProcess.onExit((exitCode) => {
102
- this.handlePTYExit(exitCode);
103
- });
104
-
105
- if (!this.silent && process.stdin.isTTY) {
106
- process.stdin.setRawMode(true);
107
- process.stdin.setEncoding('utf8');
108
- process.stdin.resume();
109
-
110
- process.stdin.on('data', (data) => {
111
- const filtered = data.toString()
112
- .replace(/\x1b\[I/g, '')
113
- .replace(/\x1b\[O/g, '');
114
-
115
- if (filtered.length > 0) {
116
- this.ptyProcess.write(filtered);
117
- }
118
-
119
- if (data === '\u0003') {
120
- this.kill();
121
- process.exit(0);
122
- }
123
- });
124
- }
125
-
126
- if (!this.silent) {
127
- this.setupLocalResizeListener();
128
- }
129
-
130
- return true;
131
- } catch (err) {
132
- logger.error(`Failed to start ${this.tool.displayName}: ${err.message}`);
133
- return false;
134
- }
135
- }
136
-
137
- // Handle PTY output
138
- handlePTYOutput(data) {
139
- let filtered = data
140
- .replace(/\x1b\[I/g, '')
141
- .replace(/\x1b\[O/g, '');
142
-
143
- if (filtered.length > 0) {
144
- if (this.debugOutputCount < 12) {
145
- this.debugOutputCount += 1;
146
- logger.debugInfo(`[pty-output] tool=${this.tool.key}; chunk=${this.debugOutputCount}; bytes=${Buffer.byteLength(filtered, 'utf8')}; preview="${previewText(filtered)}"`);
147
- }
148
-
149
- if (os.platform() === 'win32') {
150
- const now = Date.now();
151
- const hash = crypto.createHash('sha256').update(filtered).digest('hex');
152
- this.recentOutputs = this.recentOutputs.filter(entry => now - entry.timestamp < this.duplicateThresholdMs);
153
- const duplicate = this.recentOutputs.find(entry => entry.hash === hash);
154
- if (duplicate) return;
155
- this.recentOutputs.push({ hash, timestamp: now });
156
- if (this.recentOutputs.length > this.maxRecentOutputs) this.recentOutputs.shift();
157
- }
158
-
159
- if (!this.silent) {
160
- process.stdout.write(filtered);
161
- }
162
-
163
- let forMobile = filtered;
164
- if (os.platform() === 'win32' && forMobile.startsWith('\x1b[H\x1b[K')) {
165
- forMobile = '\x1b[2J\x1b[3J\x1b[H' + forMobile.slice(6);
166
- }
167
-
168
- if (!this.isTUIMode) {
169
- this.buffer.append(forMobile);
170
- }
171
-
172
- if (this.onDataCallback) {
173
- this.onDataCallback(forMobile);
174
- }
175
- }
176
- }
177
-
178
- handlePTYExit(exitCode) {
179
- logger.info(`${this.tool.displayName} exited with code ${exitCode.exitCode}`);
180
- logger.debugInfo(`[pty-exit] tool=${this.tool.key}; exitCode=${exitCode.exitCode}; signal=${exitCode.signal || ''}`);
181
- if (this.onExitCallback) {
182
- this.onExitCallback(exitCode);
183
- }
184
- }
185
-
186
- write(data) {
187
- if (this.ptyProcess) {
188
- this.ptyProcess.write(data);
189
- return true;
190
- }
191
- return false;
192
- }
193
-
194
- resize(cols, rows) {
195
- if (this.ptyProcess) {
196
- this.ptyProcess.resize(cols, rows);
197
- }
198
- }
199
-
200
- redraw(cols, rows) {
201
- if (!this.ptyProcess) return false;
202
-
203
- const targetCols = Math.max(2, Number.parseInt(cols, 10) || 80);
204
- const targetRows = Math.max(1, Number.parseInt(rows, 10) || 24);
205
- const pulseCols = targetCols > 2 ? targetCols - 1 : targetCols + 1;
206
-
207
- this.ptyProcess.resize(pulseCols, targetRows);
208
- setTimeout(() => {
209
- if (this.ptyProcess) this.ptyProcess.resize(targetCols, targetRows);
210
- }, 30);
211
- return true;
212
- }
213
-
214
- setupLocalResizeListener() {
215
- if (!process.stdout.isTTY) return;
216
- this.localResizeListener = () => {
217
- const cols = process.stdout.columns || 80;
218
- const rows = process.stdout.rows || 24;
219
- this.resize(cols, rows);
220
- };
221
- process.stdout.on('resize', this.localResizeListener);
222
- }
223
-
224
- onData(callback) {
225
- this.onDataCallback = callback;
226
- }
227
-
228
- onExit(callback) {
229
- this.onExitCallback = callback;
230
- }
231
-
232
- kill() {
233
- if (this.ptyProcess) {
234
- this.ptyProcess.kill();
235
- this.ptyProcess = null;
236
- }
237
- if (this.localResizeListener && process.stdout.off) {
238
- process.stdout.off('resize', this.localResizeListener);
239
- this.localResizeListener = null;
240
- }
241
- if (!this.silent && process.stdin.isTTY && process.stdin.setRawMode) {
242
- process.stdin.setRawMode(false);
243
- }
244
- }
245
-
246
- isRunning() {
247
- return this.ptyProcess !== null;
248
- }
249
-
250
- getPid() {
251
- return this.ptyProcess ? this.ptyProcess.pid : null;
252
- }
253
- }
254
-
255
- module.exports = PTYManager;
@@ -1,225 +0,0 @@
1
- let TerminalCtor = null;
2
-
3
- function getTerminalCtor() {
4
- if (TerminalCtor) return TerminalCtor;
5
- if (typeof global.window === 'undefined') {
6
- global.window = {};
7
- }
8
- ({ Terminal: TerminalCtor } = require('@xterm/headless'));
9
- return TerminalCtor;
10
- }
11
-
12
- class RenderedHistory {
13
- constructor(options = {}) {
14
- const Terminal = getTerminalCtor();
15
-
16
- this.maxBytes = options.maxBytes || 5 * 1024 * 1024;
17
- this.debugLabel = options.debugLabel || 'session';
18
- this.cols = Math.max(2, options.cols || 80);
19
- this.rows = Math.max(1, options.rows || 24);
20
- this.minCols = Math.max(2, options.minCols || 20);
21
- this.minRows = Math.max(1, options.minRows || 8);
22
- this.updatedAt = Date.now();
23
- this.totalWrites = 0;
24
- this.totalBytes = 0;
25
- this.pendingWrites = 0;
26
- this.resizeEvents = 0;
27
- this.truncated = false;
28
- this.lastEvents = [];
29
- this.archivedLines = [];
30
- this.archivedBytes = 0;
31
- this.term = new Terminal({
32
- cols: this.cols,
33
- rows: this.rows,
34
- scrollback: Math.max(20000, options.scrollback || 20000),
35
- allowProposedApi: true
36
- });
37
- this.hookBufferTrim();
38
- }
39
-
40
- write(data) {
41
- if (!this.term) return;
42
- if (!data) return;
43
- const text = String(data);
44
- this.totalWrites += 1;
45
- this.totalBytes += Buffer.byteLength(text, 'utf8');
46
- this.pendingWrites += 1;
47
- this.updatedAt = Date.now();
48
-
49
- this.term.write(text, () => {
50
- this.pendingWrites = Math.max(0, this.pendingWrites - 1);
51
- this.updatedAt = Date.now();
52
- });
53
- }
54
-
55
- resize(cols, rows) {
56
- if (!this.term) return;
57
- const rawCols = Number.parseInt(cols, 10);
58
- const rawRows = Number.parseInt(rows, 10);
59
- const nextCols = Math.max(2, rawCols || this.cols);
60
- const nextRows = Math.max(1, rawRows || this.rows);
61
-
62
- // Hidden or unstable layouts can briefly report pathological sizes like 10x6.
63
- // Ignore those for rendered history so we don't reflow the whole buffer into noise.
64
- if (nextCols < this.minCols || nextRows < this.minRows) {
65
- this.recordEvent(`resize ignored ${nextCols}x${nextRows}`);
66
- return;
67
- }
68
-
69
- if (nextCols === this.cols && nextRows === this.rows) return;
70
-
71
- this.cols = nextCols;
72
- this.rows = nextRows;
73
- this.resizeEvents += 1;
74
- this.updatedAt = Date.now();
75
- this.recordEvent(`resize ${this.cols}x${this.rows}`);
76
- this.term.resize(this.cols, this.rows);
77
- }
78
-
79
- toJSON() {
80
- const snapshot = this.buildSnapshot();
81
- return {
82
- text: snapshot.text,
83
- updatedAt: this.updatedAt,
84
- truncated: snapshot.truncated,
85
- bytes: snapshot.bytes,
86
- lines: snapshot.lines.length
87
- };
88
- }
89
-
90
- getDebugSnapshot(options = {}) {
91
- const tailLines = options.tailLines || 12;
92
- const snapshot = this.buildSnapshot();
93
- const active = this.term.buffer.active;
94
- return {
95
- label: this.debugLabel,
96
- updatedAt: this.updatedAt,
97
- truncated: snapshot.truncated,
98
- cols: this.cols,
99
- rows: this.rows,
100
- totalWrites: this.totalWrites,
101
- totalBytes: this.totalBytes,
102
- pendingWrites: this.pendingWrites,
103
- resizeEvents: this.resizeEvents,
104
- bufferLines: active.length,
105
- baseY: active.baseY,
106
- cursorY: active.cursorY,
107
- cursorX: active.cursorX,
108
- lastEvents: [...this.lastEvents],
109
- tailPreview: this.previewText(snapshot.lines.slice(-tailLines).join('\n'))
110
- };
111
- }
112
-
113
- dispose() {
114
- if (this.term) {
115
- this.term.dispose();
116
- this.term = null;
117
- }
118
- }
119
-
120
- hookBufferTrim() {
121
- const lines = this.term && this.term._core && this.term._core.buffer && this.term._core.buffer.lines;
122
- if (!lines || typeof lines.trimStart !== 'function') return;
123
-
124
- const originalPush = lines.push.bind(lines);
125
- lines.push = (value) => {
126
- if (lines.isFull) {
127
- const line = lines.get(0);
128
- this.archiveLines([line ? line.translateToString(true) : '']);
129
- }
130
- return originalPush(value);
131
- };
132
-
133
- if (typeof lines.recycle === 'function') {
134
- const originalRecycle = lines.recycle.bind(lines);
135
- lines.recycle = () => {
136
- if (lines.isFull) {
137
- const line = lines.get(0);
138
- this.archiveLines([line ? line.translateToString(true) : '']);
139
- }
140
- return originalRecycle();
141
- };
142
- }
143
-
144
- const originalTrimStart = lines.trimStart.bind(lines);
145
- lines.trimStart = (amount) => {
146
- const removed = [];
147
- for (let i = 0; i < amount; i++) {
148
- const line = lines.get(i);
149
- removed.push(line ? line.translateToString(true) : '');
150
- }
151
- this.archiveLines(removed);
152
- return originalTrimStart(amount);
153
- };
154
- }
155
-
156
- archiveLines(lines) {
157
- if (!lines || lines.length === 0) return;
158
- for (const line of lines) {
159
- this.archivedLines.push(line);
160
- this.archivedBytes += Buffer.byteLength(line, 'utf8') + 1;
161
- }
162
- this.trimArchivedBytes();
163
- }
164
-
165
- buildSnapshot() {
166
- const active = this.term.buffer.active;
167
- let lines = [...this.archivedLines];
168
-
169
- for (let i = 0; i < active.length; i++) {
170
- const line = active.getLine(i);
171
- lines.push(line ? line.translateToString(true) : '');
172
- }
173
-
174
- let truncated = false;
175
-
176
- let bytes = Buffer.byteLength(lines.join('\n'), 'utf8');
177
- while (bytes > this.maxBytes && lines.length > 1) {
178
- const removed = lines.shift();
179
- bytes -= Buffer.byteLength(removed, 'utf8') + 1;
180
- truncated = true;
181
- }
182
-
183
- if (bytes > this.maxBytes && lines.length === 1) {
184
- const line = lines[0];
185
- const keepChars = Math.max(1, Math.floor(this.maxBytes / 2));
186
- lines[0] = line.slice(-keepChars);
187
- bytes = Buffer.byteLength(lines[0], 'utf8');
188
- truncated = true;
189
- }
190
-
191
- const text = lines.join('\n').replace(/\s+$/g, '');
192
- this.truncated = truncated;
193
- return {
194
- lines,
195
- text,
196
- bytes: Buffer.byteLength(text, 'utf8'),
197
- truncated
198
- };
199
- }
200
-
201
- recordEvent(message) {
202
- this.lastEvents.push(`${new Date().toISOString()} ${message}`);
203
- if (this.lastEvents.length > 25) this.lastEvents.shift();
204
- }
205
-
206
- trimArchivedBytes() {
207
- while (this.archivedBytes > this.maxBytes && this.archivedLines.length > 0) {
208
- const removed = this.archivedLines.shift();
209
- this.archivedBytes -= Buffer.byteLength(removed, 'utf8') + 1;
210
- this.truncated = true;
211
- }
212
- }
213
-
214
- previewText(text, maxChars = 400) {
215
- if (!text) return '';
216
- const normalized = String(text)
217
- .replace(/\r/g, '\\r')
218
- .replace(/\n/g, '\\n')
219
- .replace(/\t/g, '\\t')
220
- .replace(/\x1b/g, '\\x1b');
221
- return normalized.length > maxChars ? normalized.slice(-maxChars) : normalized;
222
- }
223
- }
224
-
225
- module.exports = RenderedHistory;