@effected/env 0.1.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/Audience.js +94 -0
- package/EnvOverride.js +109 -0
- package/LICENSE +21 -0
- package/README.md +102 -0
- package/RuntimeEnv.js +117 -0
- package/TerminalEnv.js +144 -0
- package/index.d.ts +442 -0
- package/index.js +6 -0
- package/internal/agentCi.js +69 -0
- package/internal/colorDepth.js +128 -0
- package/internal/envRecord.js +24 -0
- package/internal/keys.js +19 -0
- package/internal/osc8/detect.js +148 -0
- package/internal/osc8/env.js +21 -0
- package/internal/osc8/semver.js +46 -0
- package/internal/osc8/terminals.js +319 -0
- package/internal/osc8/wrappers.js +23 -0
- package/package.json +48 -0
- package/tsdoc-metadata.json +11 -0
package/internal/keys.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { agentCiKeys } from "./agentCi.js";
|
|
2
|
+
import { colorKeys } from "./colorDepth.js";
|
|
3
|
+
import { terminalKeys } from "./osc8/detect.js";
|
|
4
|
+
|
|
5
|
+
//#region src/internal/keys.ts
|
|
6
|
+
/**
|
|
7
|
+
* Every environment variable any detector reads: the union of the agent/CI, colour and terminal key lists, without
|
|
8
|
+
* duplicates.
|
|
9
|
+
*
|
|
10
|
+
* @internal
|
|
11
|
+
*/
|
|
12
|
+
const allKeys = [.../* @__PURE__ */ new Set([
|
|
13
|
+
...agentCiKeys,
|
|
14
|
+
...colorKeys,
|
|
15
|
+
...terminalKeys
|
|
16
|
+
])];
|
|
17
|
+
|
|
18
|
+
//#endregion
|
|
19
|
+
export { allKeys };
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { envIsTruthy } from "./env.js";
|
|
2
|
+
import { compareSemver } from "./semver.js";
|
|
3
|
+
import { NO_CAPS, lookupTerminal } from "./terminals.js";
|
|
4
|
+
import { detectWrapper } from "./wrappers.js";
|
|
5
|
+
import { Option } from "effect";
|
|
6
|
+
|
|
7
|
+
//#region src/internal/osc8/detect.ts
|
|
8
|
+
const explanationFor = (reason, terminal, terminalVersion, wrapper) => {
|
|
9
|
+
switch (reason) {
|
|
10
|
+
case "force-env": return "FORCE_HYPERLINK env var is set";
|
|
11
|
+
case "no-hyperlink-env": return "NO_HYPERLINK env var is set";
|
|
12
|
+
case "no-color-env": return "NO_COLOR env var is set";
|
|
13
|
+
case "not-a-tty": return "stdout is not a TTY";
|
|
14
|
+
case "wrapper-strips": return `inside ${wrapper?.name ?? "wrapper"}; passthrough not verifiable without subprocess`;
|
|
15
|
+
case "terminal-known-supported": return `detected ${terminal}${terminalVersion ? ` ${terminalVersion}` : ""}`;
|
|
16
|
+
case "terminal-known-unsupported": return `detected ${terminal}; terminal does not support OSC8`;
|
|
17
|
+
case "terminal-known-too-old": return `detected ${terminal} ${terminalVersion ?? ""}; below minimum version`;
|
|
18
|
+
case "terminal-unknown": return "no identifying signal matched";
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Run the 7-rule precedence ladder for a single stream's TTY state.
|
|
23
|
+
* Shared by stdout and stderr so the two paths cannot drift.
|
|
24
|
+
*/
|
|
25
|
+
const evaluateGate = (isTTY, force, noHyperlink, noColor, wrapper, match) => {
|
|
26
|
+
if (force) return {
|
|
27
|
+
supported: true,
|
|
28
|
+
reason: "force-env",
|
|
29
|
+
override: "force-hyperlink"
|
|
30
|
+
};
|
|
31
|
+
if (noHyperlink) return {
|
|
32
|
+
supported: false,
|
|
33
|
+
reason: "no-hyperlink-env",
|
|
34
|
+
override: "no-hyperlink"
|
|
35
|
+
};
|
|
36
|
+
if (noColor) return {
|
|
37
|
+
supported: false,
|
|
38
|
+
reason: "no-color-env",
|
|
39
|
+
override: "no-color"
|
|
40
|
+
};
|
|
41
|
+
if (!isTTY) return {
|
|
42
|
+
supported: false,
|
|
43
|
+
reason: "not-a-tty",
|
|
44
|
+
override: null
|
|
45
|
+
};
|
|
46
|
+
if (wrapper) return {
|
|
47
|
+
supported: false,
|
|
48
|
+
reason: "wrapper-strips",
|
|
49
|
+
override: null
|
|
50
|
+
};
|
|
51
|
+
if (!match) return {
|
|
52
|
+
supported: false,
|
|
53
|
+
reason: "terminal-unknown",
|
|
54
|
+
override: null
|
|
55
|
+
};
|
|
56
|
+
if (!match.entry.supported) return {
|
|
57
|
+
supported: false,
|
|
58
|
+
reason: "terminal-known-unsupported",
|
|
59
|
+
override: null
|
|
60
|
+
};
|
|
61
|
+
if (match.entry.minVersion && (match.identify.version === null || compareSemver(match.identify.version, match.entry.minVersion) < 0)) return {
|
|
62
|
+
supported: false,
|
|
63
|
+
reason: "terminal-known-too-old",
|
|
64
|
+
override: null
|
|
65
|
+
};
|
|
66
|
+
return {
|
|
67
|
+
supported: true,
|
|
68
|
+
reason: "terminal-known-supported",
|
|
69
|
+
override: null
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* Determine OSC8 support from a process snapshot. Pure: same input ⇒ same
|
|
74
|
+
* output.
|
|
75
|
+
*/
|
|
76
|
+
const detect = (snap) => {
|
|
77
|
+
const { env, isStdoutTTY, isStderrTTY } = snap;
|
|
78
|
+
const wrapper = detectWrapper(env);
|
|
79
|
+
const force = envIsTruthy(env.FORCE_HYPERLINK);
|
|
80
|
+
const noHyperlink = envIsTruthy(env.NO_HYPERLINK);
|
|
81
|
+
const noColor = envIsTruthy(env.NO_COLOR, "no-color");
|
|
82
|
+
const match = lookupTerminal(env);
|
|
83
|
+
const terminal = match?.entry.name ?? null;
|
|
84
|
+
const terminalRaw = match?.identify.rawIdentifier ?? null;
|
|
85
|
+
const terminalVersion = match?.identify.version ?? null;
|
|
86
|
+
const stdout = evaluateGate(isStdoutTTY, force, noHyperlink, noColor, wrapper, match);
|
|
87
|
+
const stderr = evaluateGate(isStderrTTY, force, noHyperlink, noColor, wrapper, match);
|
|
88
|
+
const capabilities = match?.entry.capabilities ?? NO_CAPS;
|
|
89
|
+
return {
|
|
90
|
+
supported: stdout.supported,
|
|
91
|
+
supportedForStderr: stderr.supported,
|
|
92
|
+
reason: stdout.reason,
|
|
93
|
+
explanation: explanationFor(stdout.reason, terminal, terminalVersion, wrapper),
|
|
94
|
+
terminal,
|
|
95
|
+
terminalRaw,
|
|
96
|
+
terminalVersion,
|
|
97
|
+
wrapper,
|
|
98
|
+
isStdoutTTY,
|
|
99
|
+
isStderrTTY,
|
|
100
|
+
override: stdout.override,
|
|
101
|
+
capabilities
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* Project {@link detect} onto {@link Osc8Detection}: `supported` becomes
|
|
106
|
+
* `stdout`, `supportedForStderr` becomes `stderr`, and the terminal name and
|
|
107
|
+
* version become an `Option`.
|
|
108
|
+
*/
|
|
109
|
+
const detectOsc8 = (env, isStdoutTTY, isStderrTTY) => {
|
|
110
|
+
const info = detect({
|
|
111
|
+
env,
|
|
112
|
+
isStdoutTTY,
|
|
113
|
+
isStderrTTY
|
|
114
|
+
});
|
|
115
|
+
return {
|
|
116
|
+
stdout: info.supported,
|
|
117
|
+
stderr: info.supportedForStderr,
|
|
118
|
+
terminal: Option.map(Option.fromNullishOr(info.terminal), (name) => ({
|
|
119
|
+
name,
|
|
120
|
+
version: Option.fromNullishOr(info.terminalVersion)
|
|
121
|
+
}))
|
|
122
|
+
};
|
|
123
|
+
};
|
|
124
|
+
/**
|
|
125
|
+
* Every environment variable name the ported detectors read. A caller that
|
|
126
|
+
* builds an {@link Env} from `Config` reads exactly these keys.
|
|
127
|
+
*/
|
|
128
|
+
const terminalKeys = [
|
|
129
|
+
"FORCE_HYPERLINK",
|
|
130
|
+
"NO_HYPERLINK",
|
|
131
|
+
"NO_COLOR",
|
|
132
|
+
"TMUX",
|
|
133
|
+
"STY",
|
|
134
|
+
"TERM",
|
|
135
|
+
"TERM_PROGRAM",
|
|
136
|
+
"TERM_PROGRAM_VERSION",
|
|
137
|
+
"VTE_VERSION",
|
|
138
|
+
"KONSOLE_VERSION",
|
|
139
|
+
"WT_SESSION",
|
|
140
|
+
"KITTY_WINDOW_ID",
|
|
141
|
+
"TERMINAL_EMULATOR",
|
|
142
|
+
"TERMINAL_NAME",
|
|
143
|
+
"ConEmuPID",
|
|
144
|
+
"TERMINOLOGY"
|
|
145
|
+
];
|
|
146
|
+
|
|
147
|
+
//#endregion
|
|
148
|
+
export { detect, detectOsc8, terminalKeys };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
//#region src/internal/osc8/env.ts
|
|
2
|
+
const DEFAULT_FALSY = /* @__PURE__ */ new Set([
|
|
3
|
+
"0",
|
|
4
|
+
"false",
|
|
5
|
+
"off",
|
|
6
|
+
"no"
|
|
7
|
+
]);
|
|
8
|
+
/**
|
|
9
|
+
* Evaluate whether an env-var value should be considered truthy.
|
|
10
|
+
*
|
|
11
|
+
* @param value - The env-var value (commonly `process.env.SOMETHING`).
|
|
12
|
+
* @param spec - Which semantics to apply. Default: "default".
|
|
13
|
+
*/
|
|
14
|
+
const envIsTruthy = (value, spec = "default") => {
|
|
15
|
+
if (value === void 0 || value === "") return false;
|
|
16
|
+
if (spec === "no-color") return true;
|
|
17
|
+
return !DEFAULT_FALSY.has(value.toLowerCase());
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
//#endregion
|
|
21
|
+
export { envIsTruthy };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
//#region src/internal/osc8/semver.ts
|
|
2
|
+
const SEMVER_RE = /^(\d+)(?:\.(\d+))?(?:\.(\d+))?/;
|
|
3
|
+
/**
|
|
4
|
+
* Compare two semver-like strings (`MAJOR[.MINOR[.PATCH]]`). Prerelease tags
|
|
5
|
+
* (e.g. `-beta`, `-rc.1`) are stripped and ignored. Malformed inputs are
|
|
6
|
+
* treated as equal — this is a permissive helper, not a strict parser.
|
|
7
|
+
*
|
|
8
|
+
* @returns negative if `a` is less than `b`, positive if `a` is greater than `b`, 0 if equal or malformed.
|
|
9
|
+
*/
|
|
10
|
+
const compareSemver = (a, b) => {
|
|
11
|
+
const parsed = (s) => {
|
|
12
|
+
const m = SEMVER_RE.exec(s);
|
|
13
|
+
if (!m) return null;
|
|
14
|
+
return [
|
|
15
|
+
Number(m[1] ?? 0),
|
|
16
|
+
Number(m[2] ?? 0),
|
|
17
|
+
Number(m[3] ?? 0)
|
|
18
|
+
];
|
|
19
|
+
};
|
|
20
|
+
const aa = parsed(a);
|
|
21
|
+
const bb = parsed(b);
|
|
22
|
+
if (!aa || !bb) return 0;
|
|
23
|
+
for (let i = 0; i < 3; i++) {
|
|
24
|
+
const diff = (aa[i] ?? 0) - (bb[i] ?? 0);
|
|
25
|
+
if (diff !== 0) return diff;
|
|
26
|
+
}
|
|
27
|
+
return 0;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Parse a VTE_VERSION env value. VTE encodes versions as
|
|
31
|
+
* `MAJOR * 10000 + MINOR * 100 + PATCH`. So `5202` means `0.52.2`.
|
|
32
|
+
*/
|
|
33
|
+
const parseVteVersion = (raw) => {
|
|
34
|
+
if (!raw) return null;
|
|
35
|
+
const n = Number.parseInt(raw, 10);
|
|
36
|
+
if (!Number.isFinite(n) || n < 0) return null;
|
|
37
|
+
return `${Math.floor(n / 1e4)}.${Math.floor(n % 1e4 / 100)}.${n % 100}`;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Parse a KONSOLE_VERSION env value. Konsole uses calendar versioning
|
|
41
|
+
* packed identically to VTE: `YY * 10000 + MM * 100 + PATCH`.
|
|
42
|
+
*/
|
|
43
|
+
const parseKonsoleVersion = parseVteVersion;
|
|
44
|
+
|
|
45
|
+
//#endregion
|
|
46
|
+
export { compareSemver, parseKonsoleVersion, parseVteVersion };
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
import { parseKonsoleVersion, parseVteVersion } from "./semver.js";
|
|
2
|
+
|
|
3
|
+
//#region src/internal/osc8/terminals.ts
|
|
4
|
+
const NO_CAPS = {
|
|
5
|
+
params: false,
|
|
6
|
+
fileUrls: false,
|
|
7
|
+
fileUrlsRemoteUnsafe: false
|
|
8
|
+
};
|
|
9
|
+
const TERMINALS = [
|
|
10
|
+
{
|
|
11
|
+
name: "iTerm.app",
|
|
12
|
+
identify: (env) => env.TERM_PROGRAM === "iTerm.app" ? {
|
|
13
|
+
version: env.TERM_PROGRAM_VERSION ?? null,
|
|
14
|
+
rawIdentifier: env.TERM_PROGRAM
|
|
15
|
+
} : null,
|
|
16
|
+
supported: true,
|
|
17
|
+
minVersion: "3.1.0",
|
|
18
|
+
capabilities: {
|
|
19
|
+
params: true,
|
|
20
|
+
fileUrls: true,
|
|
21
|
+
fileUrlsRemoteUnsafe: false
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: "Apple_Terminal",
|
|
26
|
+
identify: (env) => env.TERM_PROGRAM === "Apple_Terminal" ? {
|
|
27
|
+
version: env.TERM_PROGRAM_VERSION ?? null,
|
|
28
|
+
rawIdentifier: env.TERM_PROGRAM
|
|
29
|
+
} : null,
|
|
30
|
+
supported: false,
|
|
31
|
+
minVersion: null,
|
|
32
|
+
capabilities: NO_CAPS
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: "VTE",
|
|
36
|
+
identify: (env) => env.VTE_VERSION ? {
|
|
37
|
+
version: parseVteVersion(env.VTE_VERSION),
|
|
38
|
+
rawIdentifier: env.VTE_VERSION
|
|
39
|
+
} : null,
|
|
40
|
+
supported: true,
|
|
41
|
+
minVersion: "0.50.0",
|
|
42
|
+
capabilities: {
|
|
43
|
+
params: false,
|
|
44
|
+
fileUrls: true,
|
|
45
|
+
fileUrlsRemoteUnsafe: false
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: "Konsole",
|
|
50
|
+
identify: (env) => env.KONSOLE_VERSION ? {
|
|
51
|
+
version: parseKonsoleVersion(env.KONSOLE_VERSION),
|
|
52
|
+
rawIdentifier: env.KONSOLE_VERSION
|
|
53
|
+
} : null,
|
|
54
|
+
supported: true,
|
|
55
|
+
minVersion: "22.4.0",
|
|
56
|
+
capabilities: {
|
|
57
|
+
params: true,
|
|
58
|
+
fileUrls: true,
|
|
59
|
+
fileUrlsRemoteUnsafe: false
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: "WezTerm",
|
|
64
|
+
identify: (env) => env.TERM_PROGRAM === "WezTerm" ? {
|
|
65
|
+
version: env.TERM_PROGRAM_VERSION ?? null,
|
|
66
|
+
rawIdentifier: env.TERM_PROGRAM
|
|
67
|
+
} : null,
|
|
68
|
+
supported: true,
|
|
69
|
+
minVersion: null,
|
|
70
|
+
capabilities: {
|
|
71
|
+
params: true,
|
|
72
|
+
fileUrls: true,
|
|
73
|
+
fileUrlsRemoteUnsafe: false
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: "kitty",
|
|
78
|
+
identify: (env) => {
|
|
79
|
+
if (env.TERM === "xterm-kitty") return {
|
|
80
|
+
version: null,
|
|
81
|
+
rawIdentifier: env.TERM
|
|
82
|
+
};
|
|
83
|
+
if (env.KITTY_WINDOW_ID) return {
|
|
84
|
+
version: null,
|
|
85
|
+
rawIdentifier: env.KITTY_WINDOW_ID
|
|
86
|
+
};
|
|
87
|
+
return null;
|
|
88
|
+
},
|
|
89
|
+
supported: true,
|
|
90
|
+
minVersion: null,
|
|
91
|
+
capabilities: {
|
|
92
|
+
params: true,
|
|
93
|
+
fileUrls: true,
|
|
94
|
+
fileUrlsRemoteUnsafe: false
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
name: "vscode",
|
|
99
|
+
identify: (env) => env.TERM_PROGRAM === "vscode" ? {
|
|
100
|
+
version: env.TERM_PROGRAM_VERSION ?? null,
|
|
101
|
+
rawIdentifier: env.TERM_PROGRAM
|
|
102
|
+
} : null,
|
|
103
|
+
supported: true,
|
|
104
|
+
minVersion: "1.71.0",
|
|
105
|
+
capabilities: {
|
|
106
|
+
params: false,
|
|
107
|
+
fileUrls: true,
|
|
108
|
+
fileUrlsRemoteUnsafe: true
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
name: "Hyper",
|
|
113
|
+
identify: (env) => env.TERM_PROGRAM === "Hyper" ? {
|
|
114
|
+
version: env.TERM_PROGRAM_VERSION ?? null,
|
|
115
|
+
rawIdentifier: env.TERM_PROGRAM
|
|
116
|
+
} : null,
|
|
117
|
+
supported: true,
|
|
118
|
+
minVersion: "3.0.0",
|
|
119
|
+
capabilities: {
|
|
120
|
+
params: false,
|
|
121
|
+
fileUrls: true,
|
|
122
|
+
fileUrlsRemoteUnsafe: false
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
name: "mintty",
|
|
127
|
+
identify: (env) => env.TERM_PROGRAM === "mintty" ? {
|
|
128
|
+
version: env.TERM_PROGRAM_VERSION ?? null,
|
|
129
|
+
rawIdentifier: env.TERM_PROGRAM
|
|
130
|
+
} : null,
|
|
131
|
+
supported: true,
|
|
132
|
+
minVersion: "3.6.0",
|
|
133
|
+
capabilities: {
|
|
134
|
+
params: true,
|
|
135
|
+
fileUrls: true,
|
|
136
|
+
fileUrlsRemoteUnsafe: false
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
name: "WindowsTerminal",
|
|
141
|
+
identify: (env) => env.WT_SESSION ? {
|
|
142
|
+
version: null,
|
|
143
|
+
rawIdentifier: env.WT_SESSION
|
|
144
|
+
} : null,
|
|
145
|
+
supported: true,
|
|
146
|
+
minVersion: null,
|
|
147
|
+
capabilities: {
|
|
148
|
+
params: false,
|
|
149
|
+
fileUrls: true,
|
|
150
|
+
fileUrlsRemoteUnsafe: false
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
name: "Alacritty",
|
|
155
|
+
identify: (env) => env.TERM === "alacritty" ? {
|
|
156
|
+
version: null,
|
|
157
|
+
rawIdentifier: env.TERM
|
|
158
|
+
} : null,
|
|
159
|
+
supported: true,
|
|
160
|
+
minVersion: null,
|
|
161
|
+
capabilities: {
|
|
162
|
+
params: false,
|
|
163
|
+
fileUrls: true,
|
|
164
|
+
fileUrlsRemoteUnsafe: false
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
name: "Ghostty",
|
|
169
|
+
identify: (env) => env.TERM_PROGRAM === "ghostty" ? {
|
|
170
|
+
version: env.TERM_PROGRAM_VERSION ?? null,
|
|
171
|
+
rawIdentifier: env.TERM_PROGRAM
|
|
172
|
+
} : null,
|
|
173
|
+
supported: true,
|
|
174
|
+
minVersion: null,
|
|
175
|
+
capabilities: {
|
|
176
|
+
params: true,
|
|
177
|
+
fileUrls: true,
|
|
178
|
+
fileUrlsRemoteUnsafe: false
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
name: "JediTerm",
|
|
183
|
+
identify: (env) => env.TERMINAL_EMULATOR === "JetBrains-JediTerm" ? {
|
|
184
|
+
version: null,
|
|
185
|
+
rawIdentifier: env.TERMINAL_EMULATOR
|
|
186
|
+
} : null,
|
|
187
|
+
supported: true,
|
|
188
|
+
minVersion: null,
|
|
189
|
+
capabilities: {
|
|
190
|
+
params: false,
|
|
191
|
+
fileUrls: true,
|
|
192
|
+
fileUrlsRemoteUnsafe: false
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
name: "Tabby",
|
|
197
|
+
identify: (env) => env.TERM_PROGRAM === "Tabby" ? {
|
|
198
|
+
version: env.TERM_PROGRAM_VERSION ?? null,
|
|
199
|
+
rawIdentifier: env.TERM_PROGRAM
|
|
200
|
+
} : null,
|
|
201
|
+
supported: true,
|
|
202
|
+
minVersion: null,
|
|
203
|
+
capabilities: {
|
|
204
|
+
params: true,
|
|
205
|
+
fileUrls: true,
|
|
206
|
+
fileUrlsRemoteUnsafe: false
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
name: "Foot",
|
|
211
|
+
identify: (env) => env.TERM === "foot" || env.TERM === "foot-extra" ? {
|
|
212
|
+
version: null,
|
|
213
|
+
rawIdentifier: env.TERM
|
|
214
|
+
} : null,
|
|
215
|
+
supported: true,
|
|
216
|
+
minVersion: null,
|
|
217
|
+
capabilities: {
|
|
218
|
+
params: true,
|
|
219
|
+
fileUrls: true,
|
|
220
|
+
fileUrlsRemoteUnsafe: false
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
name: "Rio",
|
|
225
|
+
identify: (env) => env.TERM_PROGRAM === "rio" ? {
|
|
226
|
+
version: env.TERM_PROGRAM_VERSION ?? null,
|
|
227
|
+
rawIdentifier: env.TERM_PROGRAM
|
|
228
|
+
} : null,
|
|
229
|
+
supported: true,
|
|
230
|
+
minVersion: null,
|
|
231
|
+
capabilities: {
|
|
232
|
+
params: false,
|
|
233
|
+
fileUrls: true,
|
|
234
|
+
fileUrlsRemoteUnsafe: false
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
name: "Contour",
|
|
239
|
+
identify: (env) => env.TERMINAL_NAME === "contour" ? {
|
|
240
|
+
version: null,
|
|
241
|
+
rawIdentifier: env.TERMINAL_NAME
|
|
242
|
+
} : null,
|
|
243
|
+
supported: true,
|
|
244
|
+
minVersion: null,
|
|
245
|
+
capabilities: {
|
|
246
|
+
params: true,
|
|
247
|
+
fileUrls: true,
|
|
248
|
+
fileUrlsRemoteUnsafe: false
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
name: "ConEmu",
|
|
253
|
+
identify: (env) => env.ConEmuPID ? {
|
|
254
|
+
version: null,
|
|
255
|
+
rawIdentifier: env.ConEmuPID
|
|
256
|
+
} : null,
|
|
257
|
+
supported: true,
|
|
258
|
+
minVersion: null,
|
|
259
|
+
capabilities: {
|
|
260
|
+
params: false,
|
|
261
|
+
fileUrls: true,
|
|
262
|
+
fileUrlsRemoteUnsafe: false
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
name: "WarpTerminal",
|
|
267
|
+
identify: (env) => env.TERM_PROGRAM === "WarpTerminal" ? {
|
|
268
|
+
version: env.TERM_PROGRAM_VERSION ?? null,
|
|
269
|
+
rawIdentifier: env.TERM_PROGRAM
|
|
270
|
+
} : null,
|
|
271
|
+
supported: true,
|
|
272
|
+
minVersion: null,
|
|
273
|
+
capabilities: {
|
|
274
|
+
params: false,
|
|
275
|
+
fileUrls: true,
|
|
276
|
+
fileUrlsRemoteUnsafe: false
|
|
277
|
+
}
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
name: "WaveTerminal",
|
|
281
|
+
identify: (env) => env.TERM_PROGRAM === "WaveTerminal" ? {
|
|
282
|
+
version: env.TERM_PROGRAM_VERSION ?? null,
|
|
283
|
+
rawIdentifier: env.TERM_PROGRAM
|
|
284
|
+
} : null,
|
|
285
|
+
supported: true,
|
|
286
|
+
minVersion: null,
|
|
287
|
+
capabilities: {
|
|
288
|
+
params: false,
|
|
289
|
+
fileUrls: true,
|
|
290
|
+
fileUrlsRemoteUnsafe: false
|
|
291
|
+
}
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
name: "Terminology",
|
|
295
|
+
identify: (env) => env.TERMINOLOGY === "1" ? {
|
|
296
|
+
version: null,
|
|
297
|
+
rawIdentifier: env.TERMINOLOGY
|
|
298
|
+
} : null,
|
|
299
|
+
supported: false,
|
|
300
|
+
minVersion: null,
|
|
301
|
+
capabilities: NO_CAPS
|
|
302
|
+
}
|
|
303
|
+
];
|
|
304
|
+
/**
|
|
305
|
+
* Find the first allowlist entry whose `identify()` returns non-null.
|
|
306
|
+
*/
|
|
307
|
+
const lookupTerminal = (env) => {
|
|
308
|
+
for (const entry of TERMINALS) {
|
|
309
|
+
const id = entry.identify(env);
|
|
310
|
+
if (id) return {
|
|
311
|
+
entry,
|
|
312
|
+
identify: id
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
return null;
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
//#endregion
|
|
319
|
+
export { NO_CAPS, lookupTerminal };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//#region src/internal/osc8/wrappers.ts
|
|
2
|
+
/**
|
|
3
|
+
* Detect a multiplexer wrapper from the env. Conservative — `passesThrough`
|
|
4
|
+
* is always `false` because we cannot verify version or config without
|
|
5
|
+
* spawning a subprocess, which this package never does.
|
|
6
|
+
*
|
|
7
|
+
* Users who know their tmux ≥ 3.4 has `set -g allow-passthrough on` can
|
|
8
|
+
* opt back in via FORCE_HYPERLINK=1.
|
|
9
|
+
*/
|
|
10
|
+
const detectWrapper = (env) => {
|
|
11
|
+
if (env.TMUX) return {
|
|
12
|
+
name: "tmux",
|
|
13
|
+
passesThrough: false
|
|
14
|
+
};
|
|
15
|
+
if (env.STY) return {
|
|
16
|
+
name: "screen",
|
|
17
|
+
passesThrough: false
|
|
18
|
+
};
|
|
19
|
+
return null;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
//#endregion
|
|
23
|
+
export { detectWrapper };
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@effected/env",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "Effect-native environment detection: agent, CI, terminal colour level, hyperlinks, columns and audience — read once through Config, swapped in tests with layerTest",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"effect",
|
|
8
|
+
"env",
|
|
9
|
+
"ci",
|
|
10
|
+
"agent",
|
|
11
|
+
"color",
|
|
12
|
+
"tty",
|
|
13
|
+
"osc8",
|
|
14
|
+
"hyperlinks",
|
|
15
|
+
"effected"
|
|
16
|
+
],
|
|
17
|
+
"homepage": "https://github.com/spencerbeggs/effected/tree/main/packages/env#readme",
|
|
18
|
+
"bugs": {
|
|
19
|
+
"url": "https://github.com/spencerbeggs/effected/issues"
|
|
20
|
+
},
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/spencerbeggs/effected.git",
|
|
24
|
+
"directory": "packages/env"
|
|
25
|
+
},
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"author": {
|
|
28
|
+
"name": "C. Spencer Beggs",
|
|
29
|
+
"email": "spencer@beggs.codes",
|
|
30
|
+
"url": "https://spencerbeg.gs"
|
|
31
|
+
},
|
|
32
|
+
"sideEffects": false,
|
|
33
|
+
"type": "module",
|
|
34
|
+
"exports": {
|
|
35
|
+
".": {
|
|
36
|
+
"types": "./index.d.ts",
|
|
37
|
+
"import": "./index.js",
|
|
38
|
+
"default": "./index.js"
|
|
39
|
+
},
|
|
40
|
+
"./package.json": "./package.json"
|
|
41
|
+
},
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"effect": "^4.0.0"
|
|
44
|
+
},
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=24.11.0"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// This file is read by tools that parse documentation comments conforming to the TSDoc standard.
|
|
2
|
+
// It should be published with your NPM package. It should not be tracked by Git.
|
|
3
|
+
{
|
|
4
|
+
"tsdocVersion": "0.12",
|
|
5
|
+
"toolPackages": [
|
|
6
|
+
{
|
|
7
|
+
"packageName": "@microsoft/api-extractor",
|
|
8
|
+
"packageVersion": "7.59.1"
|
|
9
|
+
}
|
|
10
|
+
]
|
|
11
|
+
}
|