@sanohiro/casty 1.1.0 → 1.1.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.
package/README.ja.md CHANGED
@@ -6,6 +6,8 @@
6
6
 
7
7
  casty は w3m や lynx のようなテキストブラウザではありません。ヘッドレス Chrome を起動し、CDP でレンダリング結果を取得して、Kitty graphics protocol でターミナルに描画します。Chrome のリモートデスクトップがターミナルに収まった感じです。
8
8
 
9
+ ![Ghostty 上で動作する casty](docs/screenshot-ghostty.png)
10
+
9
11
  <video src="https://github.com/user-attachments/assets/552f1972-bb53-481e-9516-c36b7e5085d8" autoplay loop muted playsinline></video>
10
12
 
11
13
  ## 仕組み
@@ -164,6 +166,19 @@ lib/bookmarks.js ブックマーク検索
164
166
 
165
167
  ## トラブルシューティング
166
168
 
169
+ ### YouTube で音が出ない(Ubuntu Server)
170
+
171
+ Chrome はシステムのオーディオサーバーを通じて直接音声を再生します。音が出ない場合:
172
+
173
+ ```bash
174
+ sudo apt install pulseaudio
175
+ sudo usermod -aG audio $USER
176
+ # ログアウトして再ログイン後:
177
+ pulseaudio --start
178
+ ```
179
+
180
+ ### Chrome がクラッシュする
181
+
167
182
  casty が起動しない、または Chrome がクラッシュする場合、ブラウザキャッシュを削除してください:
168
183
 
169
184
  ```bash
package/README.md CHANGED
@@ -6,6 +6,8 @@ Run a real Chrome browser inside your terminal.
6
6
 
7
7
  casty is not a text-mode browser like w3m or lynx. It launches headless Chrome, grabs the rendered frames over CDP, and draws them in your terminal via Kitty graphics protocol. Think of it as a remote desktop for Chrome that fits in a terminal window.
8
8
 
9
+ ![casty running on Ghostty](docs/screenshot-ghostty.png)
10
+
9
11
  <video src="https://github.com/user-attachments/assets/552f1972-bb53-481e-9516-c36b7e5085d8" autoplay loop muted playsinline></video>
10
12
 
11
13
  ## How It Works
@@ -164,6 +166,19 @@ lib/bookmarks.js Bookmark search
164
166
 
165
167
  ## Troubleshooting
166
168
 
169
+ ### No audio on YouTube (Ubuntu Server)
170
+
171
+ Chrome plays audio directly through the system audio server. If there's no sound:
172
+
173
+ ```bash
174
+ sudo apt install pulseaudio
175
+ sudo usermod -aG audio $USER
176
+ # Log out and back in, then:
177
+ pulseaudio --start
178
+ ```
179
+
180
+ ### Chrome crashes
181
+
167
182
  If casty fails to start or Chrome crashes, try removing the browser cache:
168
183
 
169
184
  ```bash
package/lib/browser.js CHANGED
@@ -7,8 +7,18 @@ import { CDPClient } from './cdp.js';
7
7
  import { launchChrome } from './chrome.js';
8
8
  import { loadConfig } from './config.js';
9
9
 
10
- const CHROME_VERSION = '145.0.7632.6';
11
- const USER_AGENT = `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${CHROME_VERSION} Safari/537.36`;
10
+ const DEFAULT_CHROME_VERSION = '146.0.7680.80';
11
+
12
+ // Platform-specific identity (must match real OS to avoid TLS/TCP fingerprint mismatch)
13
+ const PLATFORM = process.platform === 'darwin'
14
+ ? { ua: 'Macintosh; Intel Mac OS X 10_15_7', nav: 'MacIntel',
15
+ glVendor: 'Google Inc. (Apple)', glRenderer: 'ANGLE (Apple, Apple M1, OpenGL 4.1)' }
16
+ : { ua: `X11; Linux ${process.arch === 'arm64' ? 'aarch64' : 'x86_64'}`,
17
+ nav: `Linux ${process.arch === 'arm64' ? 'aarch64' : 'x86_64'}`,
18
+ glVendor: 'Google Inc. (Intel)', glRenderer: 'ANGLE (Intel, Mesa Intel UHD Graphics, OpenGL 4.5)' };
19
+
20
+ const buildUserAgent = (v) =>
21
+ `Mozilla/5.0 (${PLATFORM.ua}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${v} Safari/537.36`;
12
22
  const PROFILE_DIR = join(homedir(), '.casty', 'profile');
13
23
 
14
24
  // Screencast settings (low-res stream for change detection)
@@ -36,9 +46,8 @@ function buildStealthScript(lang) {
36
46
  Object.defineProperty(navigator, 'plugins', {
37
47
  get: () => {
38
48
  const arr = [
39
- { name: 'Chrome PDF Plugin', filename: 'internal-pdf-viewer', description: 'Portable Document Format' },
40
- { name: 'Chrome PDF Viewer', filename: 'mhjfbmdgcfjbbpaeojofohoefgiehjai', description: '' },
41
- { name: 'Native Client', filename: 'internal-nacl-plugin', description: '' },
49
+ { name: 'PDF Viewer', filename: 'internal-pdf-viewer', description: 'Portable Document Format', length: 1 },
50
+ { name: 'Chrome PDF Viewer', filename: 'internal-pdf-viewer', description: 'Portable Document Format', length: 1 },
42
51
  ];
43
52
  arr.refresh = () => {};
44
53
  return arr;
@@ -89,8 +98,8 @@ navigator.permissions.query = (params) => {
89
98
  // 0x9245 = UNMASKED_VENDOR_WEBGL, 0x9246 = UNMASKED_RENDERER_WEBGL
90
99
  const getParam = WebGLRenderingContext.prototype.getParameter;
91
100
  WebGLRenderingContext.prototype.getParameter = function(p) {
92
- if (p === 0x9245) return 'Google Inc. (Apple)';
93
- if (p === 0x9246) return 'ANGLE (Apple, Apple M1, OpenGL 4.1)';
101
+ if (p === 0x9245) return '${PLATFORM.glVendor}';
102
+ if (p === 0x9246) return '${PLATFORM.glRenderer}';
94
103
  return getParam.call(this, p);
95
104
  };
96
105
 
@@ -132,6 +141,11 @@ export async function setupPage({ port, wsUrl }, { width, height, zoom = 1 } = {
132
141
  const lang = config.language;
133
142
  const baseUrl = `http://127.0.0.1:${port}`;
134
143
 
144
+ // Detect Chrome version from CDP endpoint
145
+ const versionInfo = await fetchJson(`${baseUrl}/json/version`);
146
+ const chromeVersion = versionInfo?.Browser?.replace(/^Chrome\//, '') || DEFAULT_CHROME_VERSION;
147
+ const userAgent = buildUserAgent(chromeVersion);
148
+
135
149
  // Try /json/new (single HTTP request, fastest)
136
150
  let target = await fetchJson(`${baseUrl}/json/new?about:blank`);
137
151
  if (!target?.webSocketDebuggerUrl) {
@@ -162,7 +176,7 @@ export async function setupPage({ port, wsUrl }, { width, height, zoom = 1 } = {
162
176
  width: cssWidth, height: cssHeight, deviceScaleFactor: zoom, mobile: false,
163
177
  }),
164
178
  client.send('Network.setUserAgentOverride', {
165
- userAgent: USER_AGENT, platform: 'MacIntel',
179
+ userAgent: userAgent, platform: PLATFORM.nav,
166
180
  acceptLanguage: buildAcceptLanguage(lang),
167
181
  }),
168
182
  client.send('Page.addScriptToEvaluateOnNewDocument', { source: buildStealthScript(lang) }),
package/lib/config.js CHANGED
@@ -15,15 +15,16 @@ const configPath = join(homedir(), '.casty', 'config.json');
15
15
 
16
16
  // Detect system locale (e.g. "en-US", "ja", "zh-CN")
17
17
  function detectLocale() {
18
- // Intl API gives the most reliable result
18
+ // Environment variables first (Intl may lack ICU data and return en-US)
19
+ const env = process.env.LANG || process.env.LC_ALL || process.env.LC_MESSAGES || '';
20
+ const m = env.match(/^([a-z]{2})(?:[_-]([A-Z]{2}))?/);
21
+ if (m) return m[2] ? `${m[1]}-${m[2]}` : m[1];
22
+ // Fallback to Intl API
19
23
  try {
20
24
  const locale = Intl.DateTimeFormat().resolvedOptions().locale;
21
25
  if (locale) return locale;
22
26
  } catch {}
23
- // Fallback to environment variables
24
- const env = process.env.LANG || process.env.LC_ALL || process.env.LC_MESSAGES || '';
25
- const m = env.match(/^([a-z]{2}(?:[_-][A-Z]{2})?)/);
26
- return m ? m[1].replace('_', '-') : 'en-US';
27
+ return 'en-US';
27
28
  }
28
29
 
29
30
  const DEFAULTS = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanohiro/casty",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "TTY web browser using raw CDP and Kitty graphics protocol",
5
5
  "main": "bin/casty.js",
6
6
  "bin": {