terminfo.dev 1.2.0 → 1.3.0
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 +1 -1
- package/src/detect.ts +76 -26
- package/src/index.ts +2 -0
- package/src/submit.ts +4 -0
package/package.json
CHANGED
package/src/detect.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Terminal detection — identify the running terminal emulator.
|
|
3
3
|
*
|
|
4
|
-
* Uses environment variables,
|
|
4
|
+
* Uses environment variables, macOS bundle metadata, and fallback heuristics.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { release } from "node:os"
|
|
8
|
+
import { execFileSync } from "node:child_process"
|
|
8
9
|
|
|
9
10
|
export interface TerminalInfo {
|
|
10
11
|
name: string
|
|
@@ -14,7 +15,7 @@ export interface TerminalInfo {
|
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
/** Known terminal detection via environment variables */
|
|
17
|
-
const ENV_DETECTORS: Array<{ env: string; name: string
|
|
18
|
+
const ENV_DETECTORS: Array<{ env: string; name: string }> = [
|
|
18
19
|
{ env: "GHOSTTY_RESOURCES_DIR", name: "ghostty" },
|
|
19
20
|
{ env: "KITTY_WINDOW_ID", name: "kitty" },
|
|
20
21
|
{ env: "WEZTERM_EXECUTABLE", name: "wezterm" },
|
|
@@ -33,46 +34,97 @@ const TERM_PROGRAM_MAP: Record<string, string> = {
|
|
|
33
34
|
WarpTerminal: "warp",
|
|
34
35
|
}
|
|
35
36
|
|
|
37
|
+
/** Known macOS bundle IDs for version lookup */
|
|
38
|
+
const BUNDLE_IDS: Record<string, string> = {
|
|
39
|
+
ghostty: "com.mitchellh.ghostty",
|
|
40
|
+
kitty: "net.kovidgoyal.kitty",
|
|
41
|
+
iterm2: "com.googlecode.iterm2",
|
|
42
|
+
"terminal-app": "com.apple.Terminal",
|
|
43
|
+
wezterm: "org.wezfurlong.wezterm",
|
|
44
|
+
alacritty: "org.alacritty",
|
|
45
|
+
warp: "dev.warp.Warp-Stable",
|
|
46
|
+
}
|
|
47
|
+
|
|
36
48
|
export function detectTerminal(): TerminalInfo {
|
|
37
49
|
const os = detectOS()
|
|
38
50
|
const osVersion = detectOSVersion()
|
|
39
51
|
|
|
52
|
+
let name = "unknown"
|
|
53
|
+
let version = ""
|
|
54
|
+
|
|
40
55
|
// Check specific env vars first
|
|
41
|
-
for (const { env, name } of ENV_DETECTORS) {
|
|
56
|
+
for (const { env, name: n } of ENV_DETECTORS) {
|
|
42
57
|
if (process.env[env]) {
|
|
43
|
-
|
|
58
|
+
name = n
|
|
59
|
+
break
|
|
44
60
|
}
|
|
45
61
|
}
|
|
46
62
|
|
|
47
63
|
// Check $TERM_PROGRAM
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
64
|
+
if (name === "unknown") {
|
|
65
|
+
const termProgram = process.env.TERM_PROGRAM
|
|
66
|
+
if (termProgram) {
|
|
67
|
+
name = TERM_PROGRAM_MAP[termProgram] ?? termProgram.toLowerCase()
|
|
68
|
+
version = process.env.TERM_PROGRAM_VERSION ?? ""
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Check $TERMINAL_EMULATOR (Linux)
|
|
73
|
+
if (name === "unknown") {
|
|
74
|
+
const termEmu = process.env.TERMINAL_EMULATOR
|
|
75
|
+
if (termEmu) name = termEmu.toLowerCase()
|
|
53
76
|
}
|
|
54
77
|
|
|
55
|
-
//
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
return { name: termEmu.toLowerCase(), version: "", os, osVersion }
|
|
78
|
+
// Fallback: $TERM
|
|
79
|
+
if (name === "unknown") {
|
|
80
|
+
name = process.env.TERM ?? "unknown"
|
|
59
81
|
}
|
|
60
82
|
|
|
61
|
-
//
|
|
62
|
-
|
|
63
|
-
|
|
83
|
+
// On macOS, get version from app bundle if we don't have it yet
|
|
84
|
+
if (!version && os === "macos") {
|
|
85
|
+
version = getMacOSAppVersion(name)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return { name, version, os, osVersion }
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Get app version from macOS bundle metadata.
|
|
93
|
+
* Uses $__CFBundleIdentifier → mdfind → PlistBuddy.
|
|
94
|
+
*/
|
|
95
|
+
function getMacOSAppVersion(terminalName: string): string {
|
|
96
|
+
try {
|
|
97
|
+
// Try __CFBundleIdentifier first (set by the running app)
|
|
98
|
+
let bundleId = process.env.__CFBundleIdentifier
|
|
99
|
+
if (!bundleId) bundleId = BUNDLE_IDS[terminalName]
|
|
100
|
+
if (!bundleId) return ""
|
|
101
|
+
|
|
102
|
+
// Find app path from bundle ID
|
|
103
|
+
const appPath = execFileSync("mdfind", [`kMDItemCFBundleIdentifier == '${bundleId}'`], {
|
|
104
|
+
encoding: "utf-8",
|
|
105
|
+
timeout: 3000,
|
|
106
|
+
}).trim().split("\n")[0]
|
|
107
|
+
|
|
108
|
+
if (!appPath) return ""
|
|
109
|
+
|
|
110
|
+
// Read version from Info.plist
|
|
111
|
+
const version = execFileSync("/usr/libexec/PlistBuddy", [
|
|
112
|
+
"-c", "Print :CFBundleShortVersionString",
|
|
113
|
+
`${appPath}/Contents/Info.plist`,
|
|
114
|
+
], { encoding: "utf-8", timeout: 2000 }).trim()
|
|
115
|
+
|
|
116
|
+
return version
|
|
117
|
+
} catch {
|
|
118
|
+
return ""
|
|
119
|
+
}
|
|
64
120
|
}
|
|
65
121
|
|
|
66
122
|
function detectOS(): string {
|
|
67
123
|
switch (process.platform) {
|
|
68
|
-
case "darwin":
|
|
69
|
-
|
|
70
|
-
case "
|
|
71
|
-
|
|
72
|
-
case "win32":
|
|
73
|
-
return "windows"
|
|
74
|
-
default:
|
|
75
|
-
return process.platform
|
|
124
|
+
case "darwin": return "macos"
|
|
125
|
+
case "linux": return "linux"
|
|
126
|
+
case "win32": return "windows"
|
|
127
|
+
default: return process.platform
|
|
76
128
|
}
|
|
77
129
|
}
|
|
78
130
|
|
|
@@ -86,8 +138,6 @@ function detectOSVersion(): string {
|
|
|
86
138
|
|
|
87
139
|
/**
|
|
88
140
|
* Query terminal identity via DA2 (Secondary Device Attributes).
|
|
89
|
-
* Sends CSI > 0 c and parses the response.
|
|
90
|
-
*
|
|
91
141
|
* Must be called with raw mode enabled on stdin.
|
|
92
142
|
*/
|
|
93
143
|
export async function queryDA2(
|
package/src/index.ts
CHANGED
|
@@ -185,6 +185,8 @@ program
|
|
|
185
185
|
notes: data.notes,
|
|
186
186
|
responses: data.responses,
|
|
187
187
|
generated: new Date().toISOString(),
|
|
188
|
+
cliVersion: "1.3.0",
|
|
189
|
+
probeCount: ALL_PROBES.length,
|
|
188
190
|
})
|
|
189
191
|
if (url) {
|
|
190
192
|
console.log(`\x1b[32m✓ Issue created:\x1b[0m ${link(url, url)}`)
|
package/src/submit.ts
CHANGED
|
@@ -19,6 +19,8 @@ interface SubmitData {
|
|
|
19
19
|
notes: Record<string, string>
|
|
20
20
|
responses: Record<string, string>
|
|
21
21
|
generated: string
|
|
22
|
+
cliVersion?: string
|
|
23
|
+
probeCount?: number
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
async function prompt(question: string, defaultValue?: string): Promise<string> {
|
|
@@ -71,6 +73,8 @@ export async function submitResults(data: SubmitData): Promise<string | null> {
|
|
|
71
73
|
| Version | ${data.terminalVersion || "unknown"} |
|
|
72
74
|
| OS | ${data.os} ${data.osVersion || ""} |
|
|
73
75
|
| Score | ${passed}/${total} (${pct}%) |
|
|
76
|
+
| CLI Version | ${data.cliVersion ?? "unknown"} |
|
|
77
|
+
| Probes | ${data.probeCount ?? total} |
|
|
74
78
|
| Generated | ${data.generated} |
|
|
75
79
|
|
|
76
80
|
### Summary
|