@triflux/core 10.88.1 → 10.88.3

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.
@@ -1,6 +1,6 @@
1
1
  // hub/lib/jev-addon.mjs
2
2
  // Opt-in Jev (System One) 판단 애드온.
3
- // JEV_API_KEY / TYPESAFE_API_KEY 환경변수 또는 jev CLI(TFX_JEV=1)가 있을 때만 동작하고,
3
+ // 환경변수, 명시 CLI 또는 host 설정으로 활성화하며,
4
4
  // 설정이 없거나 호출이 실패하면 항상 null을 반환해 기존 결정론적 로직을 그대로 유지한다.
5
5
 
6
6
  import { execFile } from "node:child_process";
@@ -9,6 +9,7 @@ import { homedir } from "node:os";
9
9
  import { dirname, join } from "node:path";
10
10
  import { promisify } from "node:util";
11
11
  import { whichCommand } from "../platform.mjs";
12
+ import { readJevHostApiKey, readJevHostConfig } from "./jev-host-config.mjs";
12
13
 
13
14
  const execFileAsync = promisify(execFile);
14
15
 
@@ -41,7 +42,12 @@ function isExplicitlyEnabled(env = process.env) {
41
42
 
42
43
  export function resolveJevApiKey(env = process.env) {
43
44
  const key = env.JEV_API_KEY || env.TYPESAFE_API_KEY || "";
44
- return String(key).trim() || null;
45
+ return (
46
+ String(key).trim() ||
47
+ (isExplicitlyDisabled(env) || resolveJevCliBin(env)
48
+ ? null
49
+ : readJevHostApiKey(env))
50
+ );
45
51
  }
46
52
 
47
53
  export function resolveJevCliBin(env = process.env) {
@@ -75,6 +81,8 @@ function isDirectEnvObject(obj) {
75
81
  return false;
76
82
  }
77
83
  return (
84
+ "HOME" in obj ||
85
+ "XDG_CONFIG_HOME" in obj ||
78
86
  "TFX_JEV" in obj ||
79
87
  "JEV_API_KEY" in obj ||
80
88
  "TYPESAFE_API_KEY" in obj ||
@@ -102,8 +110,9 @@ export function isJevEnabled(options = {}) {
102
110
  ) {
103
111
  return true;
104
112
  }
105
- if (resolveJevApiKey(env)) return true;
113
+ if (String(env.JEV_API_KEY || env.TYPESAFE_API_KEY || "").trim()) return true;
106
114
  if (resolveJevCliBin(env)) return true;
115
+ if (readJevHostConfig(env)) return true;
107
116
  return false;
108
117
  }
109
118
 
@@ -0,0 +1,76 @@
1
+ import { closeSync, constants, fstatSync, openSync, readSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { isAbsolute, join } from "node:path";
4
+
5
+ const MAX_FILE_BYTES = 64 * 1024;
6
+
7
+ function readConfigFile(path, privateFile = false) {
8
+ let fd;
9
+ try {
10
+ fd = openSync(
11
+ path,
12
+ constants.O_RDONLY |
13
+ (constants.O_NOFOLLOW || 0) |
14
+ (constants.O_NONBLOCK || 0),
15
+ );
16
+ const stat = fstatSync(fd);
17
+ if (!stat.isFile() || stat.size > MAX_FILE_BYTES) return null;
18
+ if (typeof process.getuid !== "function" || stat.uid !== process.getuid())
19
+ return null;
20
+ if (
21
+ privateFile ? (stat.mode & 0o7777) !== 0o600 : (stat.mode & 0o022) !== 0
22
+ )
23
+ return null;
24
+ const buffer = Buffer.alloc(MAX_FILE_BYTES + 1);
25
+ const size = readSync(fd, buffer, 0, buffer.length, 0);
26
+ return size <= MAX_FILE_BYTES ? buffer.toString("utf8", 0, size) : null;
27
+ } catch {
28
+ return null;
29
+ } finally {
30
+ if (fd !== undefined) closeSync(fd);
31
+ }
32
+ }
33
+
34
+ export function readJevHostConfig(env = process.env) {
35
+ // 명시 env fixture는 주입한 HOME/XDG만 사용한다. 빈 객체는 실제 홈을 읽지 않는다.
36
+ const configHome =
37
+ env.XDG_CONFIG_HOME ||
38
+ (env.HOME
39
+ ? join(env.HOME, ".config")
40
+ : env === process.env
41
+ ? join(homedir(), ".config")
42
+ : null);
43
+ if (!configHome || !isAbsolute(configHome)) return null;
44
+ try {
45
+ const config = JSON.parse(
46
+ readConfigFile(join(configHome, "triflux", "jev.json")),
47
+ );
48
+ return config?.enabled === true ? config : null;
49
+ } catch {
50
+ return null;
51
+ }
52
+ }
53
+
54
+ export function readJevHostApiKey(env = process.env) {
55
+ const config = readJevHostConfig(env);
56
+ if (typeof config?.apiKeyFile !== "string" || !isAbsolute(config.apiKeyFile))
57
+ return null;
58
+ const text = readConfigFile(config.apiKeyFile, true);
59
+ if (text == null) return null;
60
+ const keys = {};
61
+ for (const line of text.split(/\r?\n/)) {
62
+ const assignment = line.match(
63
+ /^\s*(?:export\s+)?(JEV_API_KEY|TYPESAFE_API_KEY)\s*=(.*)$/,
64
+ );
65
+ if (!assignment) continue;
66
+ const [, name, raw] = assignment;
67
+ const literal = raw
68
+ .trim()
69
+ .match(/^(?:'([^']*)'|"([^"]*)"|([^\s'";#]+))\s*(?:#.*)?$/);
70
+ if (!literal || name in keys) return null;
71
+ const value = (literal[1] ?? literal[2] ?? literal[3]).trim();
72
+ if (!value || /[$`\\\x00-\x1f\x7f]/.test(value)) return null;
73
+ keys[name] = value;
74
+ }
75
+ return keys.JEV_API_KEY || keys.TYPESAFE_API_KEY || null;
76
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@triflux/core",
3
- "version": "10.88.1",
3
+ "version": "10.88.3",
4
4
  "description": "triflux core — CLI routing, adapters. Zero native dependencies.",
5
5
  "type": "module",
6
6
  "main": "hub/index.mjs",