charge-connect 0.1.2 → 0.1.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.
package/cli.js CHANGED
@@ -85,6 +85,41 @@ function installSchedule(dir) {
85
85
  }
86
86
  }
87
87
 
88
+ // ccusage가 전역에 없어도 수집은 npx 폴백으로 동작하지만, 5분마다 npx 해석을 거쳐
89
+ // 느려진다. 사용자가 터미널 앞에 있는 페어링 시점에 한 번 물어보고 깔아준다.
90
+ async function offerCcusageInstall() {
91
+ // Windows에서 ccusage/npm은 .cmd 셔틀이라 cmd.exe /d /c 로 감싼다 — shell:true의
92
+ // DEP0190 경고를 피하면서 AutoRun도 억제하는, collect.js의 runAsync와 같은 방식.
93
+ const run = (cmd, args, opts) =>
94
+ execFileSync(WIN ? process.env.ComSpec ?? "cmd.exe" : cmd, WIN ? ["/d", "/c", cmd, ...args] : args, opts);
95
+ try {
96
+ run("ccusage", ["--version"], { stdio: "ignore", timeout: 15_000 });
97
+ return; // 이미 설치돼 있다
98
+ } catch {}
99
+ const hint = "'npm i -g ccusage'를 해두면 수집이 빨라집니다";
100
+ // 파이프/CI처럼 물어볼 콘솔이 없으면 안내만 하고 넘어간다
101
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
102
+ console.log(`참고: ${hint} (지금은 npx 대체 실행으로 동작).`);
103
+ return;
104
+ }
105
+ const rl = require("node:readline/promises").createInterface({ input: process.stdin, output: process.stdout });
106
+ const answer = (await rl.question("수집 도구 ccusage를 전역 설치할까요? 수집이 빨라집니다 (Y/n) ")).trim().toLowerCase();
107
+ rl.close();
108
+ if (answer === "n" || answer === "no") {
109
+ console.log(`건너뜁니다 — 나중에 ${hint}.`);
110
+ return;
111
+ }
112
+ try {
113
+ run("npm", ["install", "-g", "ccusage"], { stdio: "inherit", timeout: 300_000 });
114
+ console.log("✓ ccusage 설치 완료");
115
+ } catch {
116
+ console.error(
117
+ "설치에 실패했지만 수집은 npx 대체 실행으로 계속 동작합니다." +
118
+ (WIN ? ` 나중에 ${hint}.` : ` 나중에 ${hint} (권한 오류라면 'sudo npm i -g ccusage').`)
119
+ );
120
+ }
121
+ }
122
+
88
123
  (async () => {
89
124
  const arg = process.argv[2];
90
125
  if (!arg) {
@@ -117,6 +152,9 @@ function installSchedule(dir) {
117
152
  else console.error(`자동 수집 등록 실패: ${e.message ?? e}`);
118
153
  }
119
154
 
155
+ // 스케줄 등록 뒤에 물어본다 — 프롬프트에서 사용자가 자리를 비워도 자동 수집은 이미 살아 있다
156
+ await offerCcusageInstall();
157
+
120
158
  console.log("첫 수집을 실행합니다…");
121
159
  let collected = true;
122
160
  try {
package/collect.js CHANGED
@@ -65,18 +65,27 @@ const DEFAULT_CODEXBAR_CLI = "/Applications/CodexBar.app/Contents/Helpers/CodexB
65
65
 
66
66
  // 모든 외부 명령은 비동기로 실행한다 — 동기(execFileSync)는 이벤트 루프를 세워
67
67
  // 진행 중인 fetch(AbortSignal 타이머 포함)를 전부 타임아웃시킨다.
68
- // Windows에서 npx/ccusage는 .cmd 셔틀이라 셸을 거쳐야 실행된다 (인자는 모두 고정 문자열).
69
68
  const execOpts = (timeout) => ({
70
69
  encoding: "utf8",
71
70
  maxBuffer: 64 * 1024 * 1024,
72
71
  timeout,
73
72
  env: { ...process.env, PATH: `${process.env.PATH}${EXTRA_PATH}` },
74
- shell: WIN,
75
73
  });
76
74
 
77
75
  async function runAsync(cmd, args, timeout = 120_000) {
76
+ // Windows에서 npx/ccusage는 .cmd 셔틀이라 셸 없이는 실행되지 않는데, shell:true에
77
+ // 인자 배열을 주면 Node 24부터 DEP0190 경고가 붙는다. 그래서 ComSpec /d /c 로 직접
78
+ // 감싼다 — /d는 shell:true도 내부에서 쓰던 AutoRun 억제로, 사용자 레지스트리의
79
+ // AutoRun 출력(chcp·clink 등)이 JSON 파싱을 깨는 것을 막는다. 경로로 주어진 명령
80
+ // (CodexBar CLI 같은 .exe)은 셸이 필요 없고 cmd 인용 규칙이 공백·괄호 경로를
81
+ // 깨뜨리므로 직접 실행한다. cmd로 넘기는 인자는 모두 고정 문자열이다.
82
+ // 한계: .cmd/.bat 경로는 cmd 경유가 불가피해 특수문자(공백+괄호, &) 경로는 지원하지 않는다.
83
+ const viaCmd = WIN && (!/[\\/]/.test(cmd) || /\.(cmd|bat)$/i.test(cmd));
84
+ const [file, argv] = viaCmd
85
+ ? [process.env.ComSpec ?? "cmd.exe", ["/d", "/c", cmd, ...args]]
86
+ : [cmd, args];
78
87
  // promisify된 execFile은 실패 시에도 err.stdout/stderr를 붙여준다
79
- return (await execFileAsync(cmd, args, execOpts(timeout))).stdout;
88
+ return (await execFileAsync(file, argv, execOpts(timeout))).stdout;
80
89
  }
81
90
 
82
91
  async function runCcusage(args) {
@@ -534,7 +543,8 @@ function parseCodexBarJSON(raw) {
534
543
 
535
544
  async function collectCodexBarProviders() {
536
545
  if (process.env.CHARGE_DISABLE_CODEXBAR === "1") return { providers: [], complete: false };
537
- const cli = process.env.CHARGE_CODEXBAR_CLI
546
+ // 따옴표로 감싼 경로(Windows에서 흔한 습관)는 벗겨서 execFile에 그대로 넘긴다
547
+ const cli = process.env.CHARGE_CODEXBAR_CLI?.replace(/^"(.*)"$/s, "$1")
538
548
  ?? (!WIN && fs.existsSync(DEFAULT_CODEXBAR_CLI) ? DEFAULT_CODEXBAR_CLI : null);
539
549
  if (!cli) return { providers: [], complete: false };
540
550
 
@@ -708,6 +718,7 @@ module.exports = {
708
718
  normalizeResetAt,
709
719
  parseCodexBarJSON,
710
720
  resolveMode,
721
+ runAsync,
711
722
  sanitizeCachedProvider,
712
723
  titleCaseProvider,
713
724
  usefulPlan,
package/install.ps1 CHANGED
@@ -85,10 +85,19 @@ function Test-ChargeTaskRuns {
85
85
  }
86
86
 
87
87
  $background = $true
88
+ $adminRetryHint = $false
88
89
  try {
89
90
  Register-ChargeTask -Background
90
91
  } catch {
91
92
  Write-Host "백그라운드 실행 등록 실패: $($_.Exception.Message)"
93
+ # 비관리자 PowerShell에서는 S4U(창 없는) 등록이 항상 거부된다 — 같은 계정을 관리자
94
+ # 권한으로 승격해 재실행하면 해결되므로 그때만 안내한다. 판별은 토큰 Claims의
95
+ # Administrators SID(S-1-5-32-544)로 한다 — UAC 필터드 토큰에서 이 그룹은 deny-only로
96
+ # 강등되는데 .Groups는 deny-only를 걸러내 버리고, .Claims에는 DenyOnlySid로 남는다.
97
+ # 표준 사용자(Claims에 아예 없음)에게 안내하면 다른 관리자 계정으로 승격해 그 계정
98
+ # 프로필로 작업을 덮어쓰게 되고, 아래 Test-ChargeTaskRuns 폴백(SeBatchLogonRight 부재)은
99
+ # 승격으로 해결되지 않는다 — 두 경우 모두 안내하지 않는다.
100
+ $adminRetryHint = [Security.Principal.WindowsIdentity]::GetCurrent().Claims.Value -contains "S-1-5-32-544"
92
101
  $background = $false
93
102
  }
94
103
  if ($background -and -not (Test-ChargeTaskRuns)) {
@@ -105,6 +114,10 @@ if (-not $background) {
105
114
  exit 1
106
115
  }
107
116
  Write-Host "참고: 5분마다 콘솔 창이 잠깐 보일 수 있습니다 (백그라운드 실행 권한이 없는 환경)."
117
+ if ($adminRetryHint) {
118
+ Write-Host "창이 뜨지 않게 하려면: 관리자 권한 PowerShell에서 아래를 한 번 실행하세요."
119
+ Write-Host " powershell -ExecutionPolicy Bypass -File `"$scriptDir\install.ps1`""
120
+ }
108
121
  }
109
122
 
110
123
  Write-Host "등록 완료: $taskName (5분 간격). 로그: $log"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "charge-connect",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Connects your computer to the Charge iPhone app and uploads AI coding usage",
5
5
  "bin": {
6
6
  "charge-connect": "cli.js"