@clazic/urban 0.2.13 → 0.2.15

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clazic/urban",
3
- "version": "0.2.13",
3
+ "version": "0.2.15",
4
4
  "description": "도시계획연구 보고서 자동 수집·지식베이스 데몬",
5
5
  "type": "module",
6
6
  "engines": {
@@ -69,25 +69,36 @@ WantedBy=default.target
69
69
 
70
70
  /**
71
71
  * 순수 함수: Windows PowerShell 커맨드 생성
72
+ *
73
+ * VBScript 방식 폐기 이유:
74
+ * - Set-Content -Encoding ASCII가 한글 사용자 경로를 깨뜨림 (조용히 실패)
75
+ * - oWsh.Run True(동기) + RestartCount 충돌 → 0xC000013A CTRL+BREAK 강제 종료
76
+ * - stdout/stderr 버려져 진단 불가
77
+ *
78
+ * 새 방식: cmd.exe /c + S4U Principal + -Hidden
79
+ * - S4U(Service For User): 비밀번호 없이 비대화형 세션에서 실행 → 콘솔 창 절대 안 뜸
80
+ * - cmd.exe로 URBAN_HOME 환경변수 주입 + 로그 리다이렉트
81
+ * - -Hidden: 작업 자체를 숨김
72
82
  */
73
- export function buildPowerShellCmd({ nodePath, daemonJs }) {
74
- // PowerShell 단따옴표 이스케이프: ' ''
75
- const psEscape = (s) => s.replace(/'/g, "''");
76
- const escapedNodePath = psEscape(nodePath);
77
- const escapedDaemonJs = psEscape(daemonJs);
83
+ export function buildPowerShellCmd({ nodePath, daemonJs, urbanHome, logsDir }) {
84
+ const psQ = (s) => String(s).replace(/'/g, "''");
85
+
86
+ const outLog = join(logsDir, 'out.log');
87
+ const errLog = join(logsDir, 'err.log');
78
88
 
79
89
  return `
80
- $action = New-ScheduledTaskAction -Execute '${escapedNodePath}' -Argument '"${escapedDaemonJs}"'
81
- $trigger = New-ScheduledTaskTrigger -AtLogOn
82
- $settings = New-ScheduledTaskSettingsSet \`
83
- -ExecutionTimeLimit 0 \`
84
- -AllowStartIfOnBatteries \`
85
- -DontStopIfGoingOnBatteries \`
86
- -StartWhenAvailable \`
87
- -MultipleInstances IgnoreNew \`
88
- -RestartCount 5 \`
89
- -RestartInterval (New-TimeSpan -Minutes 1)
90
- Register-ScheduledTask -TaskName 'Urban' -Action $action -Trigger $trigger -Settings $settings -Force
90
+ $nodePath = '${psQ(nodePath)}'
91
+ $daemonJs = '${psQ(daemonJs)}'
92
+ $urbanHome = '${psQ(urbanHome)}'
93
+ $outLog = '${psQ(outLog)}'
94
+ $errLog = '${psQ(errLog)}'
95
+ $cmdArgs = '/c set "URBAN_HOME=' + $urbanHome + '" && "' + $nodePath + '" "' + $daemonJs + '" 1>>"' + $outLog + '" 2>>"' + $errLog + '"'
96
+ $action = New-ScheduledTaskAction -Execute 'cmd.exe' -Argument $cmdArgs -WorkingDirectory $urbanHome
97
+ $trigger = New-ScheduledTaskTrigger -AtLogOn -User $env:USERNAME
98
+ $principal = New-ScheduledTaskPrincipal -UserId $env:USERNAME -LogonType S4U -RunLevel Limited
99
+ $settings = New-ScheduledTaskSettingsSet -ExecutionTimeLimit ([TimeSpan]::Zero) -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -MultipleInstances IgnoreNew -RestartCount 5 -RestartInterval (New-TimeSpan -Minutes 1) -Hidden
100
+ Unregister-ScheduledTask -TaskName 'Urban' -Confirm:$false -ErrorAction SilentlyContinue | Out-Null
101
+ Register-ScheduledTask -TaskName 'Urban' -Action $action -Trigger $trigger -Principal $principal -Settings $settings | Out-Null
91
102
  `;
92
103
  }
93
104
 
@@ -128,15 +139,23 @@ export async function installDaemon() {
128
139
  return { ok: true, message: `macOS 데몬 등록 완료: ${plistPath}` };
129
140
  } else if (isWindows) {
130
141
  // Windows: Task Scheduler (PowerShell)
131
- const ps = buildPowerShellCmd({ nodePath, daemonJs });
142
+ const ps = buildPowerShellCmd({ nodePath, daemonJs, urbanHome: URBAN_HOME, logsDir });
132
143
  try {
133
144
  // -EncodedCommand: Base64(UTF-16LE) — 따옴표/특수문자 인젝션 방지
145
+ // stdio: pipe로 PowerShell 에러 메시지 캡처해 진단 가능하게
134
146
  const encoded = Buffer.from(ps, 'utf16le').toString('base64');
135
- execSync(`powershell -NoProfile -EncodedCommand ${encoded}`, { shell: false, stdio: 'ignore' });
147
+ execSync(`powershell -NoProfile -ExecutionPolicy Bypass -EncodedCommand ${encoded}`, {
148
+ shell: false,
149
+ stdio: ['ignore', 'pipe', 'pipe'],
150
+ });
136
151
  } catch (err) {
137
- // PowerShell 실행 권한 문제 가능 메시지에 명시
138
- return { ok: false, message: `Windows 데몬 등록 실패 (관리자 권한 필요): ${err.message}` };
152
+ const stderr = err.stderr ? err.stderr.toString('utf8').trim() : '';
153
+ return { ok: false, message: `Windows 데몬 등록 실패: ${err.message}${stderr ? '\n ' + stderr : ''}` };
139
154
  }
155
+ // AtLogOn 트리거는 다음 로그온까지 대기 → 즉시 1회 시작
156
+ try {
157
+ execSync(`powershell -NoProfile -Command "Start-ScheduledTask -TaskName 'Urban'"`, { shell: true, stdio: 'ignore' });
158
+ } catch { /* 이미 실행 중이면 무시 */ }
140
159
  return { ok: true, message: `Windows 데몬 등록 완료 (작업 스케줄러: Urban)` };
141
160
  } else if (isLinux) {
142
161
  // Linux: systemd user service