@zsukim/ctv-run 1.0.11 → 1.0.13

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/README.md CHANGED
@@ -90,6 +90,7 @@
90
90
  | `appId` | | Tizen 앱 고유 ID (없으면 자동 생성) |
91
91
  | `tizenPath` | | tizen CLI 실행 경로 (기본값: `tizen`) |
92
92
  | `sdbPath` | | sdb 실행 경로 (기본값: `sdb`) |
93
+ | `configXmlPath` | | 사용할 config.xml 경로 (우선순위: dist > 지정경로 > 루트 > 기본 생성) |
93
94
 
94
95
  **WebOS**
95
96
 
@@ -99,6 +100,7 @@
99
100
  | `url` | | TV가 로드할 URL |
100
101
  | `port` | | TV가 로드할 URL의 포트 |
101
102
  | `appId` | | webOS 앱 ID (기본값: `com.ctvrun.app`) |
103
+ | `appInfoPath` | | 사용할 appinfo.json 경로 (우선순위: dist > 지정경로 > 루트 > 기본 생성) |
102
104
 
103
105
  **Common**
104
106
 
@@ -19,9 +19,13 @@ const path_1 = __importDefault(require("path"));
19
19
  const config_1 = require("../utils/config");
20
20
  const ip_1 = require("../utils/ip");
21
21
  const platform_1 = require("../utils/platform");
22
+ const DEFAULT_APP_ID = 'A123456789.CtvRunApp';
23
+ const DEFAULT_APP_NAME = 'CtvRunApp';
24
+ const DEFAULT_CERT_FILE = 'ctv-run-cert';
25
+ const DEFAULT_CERT_PROFILE = 'ctv-run-profile';
22
26
  function runTizen() {
23
27
  return __awaiter(this, void 0, void 0, function* () {
24
- var _a, _b, _c, _d, _e, _f, _g;
28
+ var _a, _b, _c, _d, _e, _f, _g, _h;
25
29
  const config = (0, config_1.loadConfig)();
26
30
  const args = process.argv.slice(2);
27
31
  const getArg = (0, platform_1.makeArgParser)(args);
@@ -79,10 +83,11 @@ function runTizen() {
79
83
  const appId = getArg('appId') ||
80
84
  (xmlInfo === null || xmlInfo === void 0 ? void 0 : xmlInfo.appId) ||
81
85
  ((_g = config.tizen) === null || _g === void 0 ? void 0 : _g.appId) ||
82
- 'A123456789.CtvRunApp';
83
- const safeAppName = ((xmlInfo === null || xmlInfo === void 0 ? void 0 : xmlInfo.appName) || 'CtvRunApp').replace(/\s+/g, '');
86
+ DEFAULT_APP_ID;
87
+ const safeAppName = ((xmlInfo === null || xmlInfo === void 0 ? void 0 : xmlInfo.appName) || DEFAULT_APP_NAME).replace(/\s+/g, '');
84
88
  // static mode는 dist의 config.xml 유지, live mode는 개발서버 URL로 새로 생성
85
- yield ensureTizenFiles(appId, tempAppDir, safeAppName, !isLiveMode, isLiveMode ? appUrl : '');
89
+ const configXmlPath = getArg('configXmlPath') || ((_h = config.tizen) === null || _h === void 0 ? void 0 : _h.configXmlPath);
90
+ yield ensureTizenFiles(appId, tempAppDir, safeAppName, !isLiveMode, isLiveMode ? appUrl : '', configXmlPath);
86
91
  // Tizen 인증서 프로필 확인/생성/활성화
87
92
  console.log('\n🎫 Tizen 인증서 프로필 확인 중...');
88
93
  const profilesXmlPath = path_1.default.join(process.env.HOME || '', 'tizen-studio-data/profile/profiles.xml');
@@ -122,7 +127,7 @@ function runTizen() {
122
127
  existingProfiles.forEach((p, i) => {
123
128
  console.log(` ${i + 1}. ${p}`);
124
129
  });
125
- const answer = askQuestion('🔑 사용할 프로필 번호를 입력하세요 (새로운 ctv-run-cert 생성): ');
130
+ const answer = askQuestion(`🔑 사용할 프로필 번호를 입력하세요 (새로운 전용 인증서 ${DEFAULT_CERT_FILE} 생성): `);
126
131
  const idx = parseInt(answer, 10) - 1;
127
132
  if (!isNaN(idx) && idx >= 0 && idx < existingProfiles.length) {
128
133
  const chosen = existingProfiles[idx];
@@ -132,8 +137,8 @@ function runTizen() {
132
137
  }
133
138
  if (!activeProfile) {
134
139
  console.log('⚠️ 활성화된 프로필이 없습니다. 앱 전용 인증서를 세팅합니다.');
135
- const certFileName = 'ctv-run-cert';
136
- const profileName = 'ctv-run-profile';
140
+ const certFileName = DEFAULT_CERT_FILE;
141
+ const profileName = DEFAULT_CERT_PROFILE;
137
142
  const homeDir = process.env.HOME || '';
138
143
  const defaultCertDir = path_1.default.join(homeDir, 'tizen-studio-data/keystore/author');
139
144
  const p12Path = path_1.default.join(defaultCertDir, `${certFileName}.p12`);
@@ -204,7 +209,8 @@ function runTizen() {
204
209
  console.error(`\n❌ 오류 발생: ${err.message}`);
205
210
  }
206
211
  finally {
207
- // if (fs.existsSync(tempAppDir)) fs.removeSync(tempAppDir);
212
+ if (fs_extra_1.default.existsSync(tempAppDir))
213
+ fs_extra_1.default.removeSync(tempAppDir);
208
214
  }
209
215
  });
210
216
  }
@@ -231,21 +237,28 @@ function getInfoFromXml(dir) {
231
237
  return null;
232
238
  }
233
239
  function ensureTizenFiles(appId_1, tempDir_1, appName_1) {
234
- return __awaiter(this, arguments, void 0, function* (appId, tempDir, appName, keepExistingConfig = false, liveUrl = '') {
235
- const configXmlPath = path_1.default.join(tempDir, 'config.xml');
236
- if (keepExistingConfig && fs_extra_1.default.existsSync(configXmlPath)) {
240
+ return __awaiter(this, arguments, void 0, function* (appId, tempDir, appName, keepExistingConfig = false, liveUrl = '', specifiedPath) {
241
+ const destConfigXml = path_1.default.join(tempDir, 'config.xml');
242
+ if (keepExistingConfig && fs_extra_1.default.existsSync(destConfigXml)) {
237
243
  // static mode: dist의 config.xml을 그대로 유지
238
244
  (0, platform_1.resolveIcon)(tempDir);
239
245
  return;
240
246
  }
241
- // live mode: 기존 config.xml 삭제 새로 생성
242
- if (fs_extra_1.default.existsSync(configXmlPath)) {
243
- fs_extra_1.default.removeSync(configXmlPath);
244
- }
245
- console.log(`📝 config.xml 생성 중... (Name: ${appName}, ID: ${appId})`);
246
- // appId 추출
247
- const packageId = appId.split('.')[0] || 'TizenApp';
248
- const xmlContent = `<?xml version="1.0" encoding="UTF-8"?>
247
+ // config.xml 없으면 지정경로 루트 → 기본값 (우선순위: dist > 지정경로 > 루트 > 기본값)
248
+ if (!fs_extra_1.default.existsSync(destConfigXml)) {
249
+ const candidates = [
250
+ specifiedPath && path_1.default.resolve(process.cwd(), specifiedPath),
251
+ path_1.default.join(process.cwd(), 'config.xml'),
252
+ ].filter(Boolean);
253
+ const found = candidates.find((p) => fs_extra_1.default.existsSync(p));
254
+ if (found) {
255
+ fs_extra_1.default.copyFileSync(found, destConfigXml);
256
+ console.log(`📝 config.xml 복사: ${found}`);
257
+ }
258
+ else {
259
+ console.log(`📝 config.xml 생성 중... (Name: ${appName}, ID: ${appId})`);
260
+ const packageId = appId.split('.')[0] || 'TizenApp';
261
+ const xmlContent = `<?xml version="1.0" encoding="UTF-8"?>
249
262
  <widget xmlns:tizen="http://tizen.org/ns/widgets" xmlns="http://www.w3.org/ns/widgets" id="http://yourdomain/tizen/${appName}" version="1.0.0" viewmodes="maximized">
250
263
  <tizen:application id="${appId}" package="${packageId}" required_version="2.1"/>
251
264
  <content src="index.html"/>
@@ -259,7 +272,9 @@ function ensureTizenFiles(appId_1, tempDir_1, appName_1) {
259
272
  <tizen:privilege name="http://tizen.org/privilege/tv.inputdevice" />
260
273
  <tizen:setting screen-orientation="landscape" context-menu="enable" background-support="disable" encryption="disable" install-location="auto" hwkey-event="enable"/>
261
274
  </widget>`;
262
- fs_extra_1.default.writeFileSync(configXmlPath, xmlContent, 'utf8');
275
+ fs_extra_1.default.writeFileSync(destConfigXml, xmlContent, 'utf8');
276
+ }
277
+ }
263
278
  if (liveUrl) {
264
279
  const redirectHtml = `<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><title>Loading...</title><meta http-equiv="refresh" content="0; url=${liveUrl}"></head><body style="background:#000;"></body></html>`;
265
280
  fs_extra_1.default.writeFileSync(path_1.default.join(tempDir, 'index.html'), redirectHtml);
@@ -19,20 +19,23 @@ const path_1 = __importDefault(require("path"));
19
19
  const config_1 = require("../utils/config");
20
20
  const ip_1 = require("../utils/ip");
21
21
  const platform_1 = require("../utils/platform");
22
+ const DEFAULT_APP_ID = 'com.ctvrun.app';
22
23
  function runWebOS() {
23
24
  return __awaiter(this, void 0, void 0, function* () {
24
- var _a, _b, _c, _d, _e;
25
+ var _a, _b, _c, _d, _e, _f;
25
26
  const config = (0, config_1.loadConfig)();
26
27
  const args = process.argv.slice(2);
27
28
  const getArg = (0, platform_1.makeArgParser)(args);
28
29
  // 기본 설정값 로드
29
30
  const deviceName = getArg('device') || ((_a = config.webos) === null || _a === void 0 ? void 0 : _a.deviceName);
30
- const appId = getArg('appId') || ((_b = config.webos) === null || _b === void 0 ? void 0 : _b.appId) || 'com.ctvrun.app';
31
+ const appId = getArg('appId') || ((_b = config.webos) === null || _b === void 0 ? void 0 : _b.appId) || DEFAULT_APP_ID;
32
+ const appInfoPath = getArg('appInfoPath') || ((_c = config.webos) === null || _c === void 0 ? void 0 : _c.appInfoPath);
31
33
  const targetArg = getArg('target');
32
- const port = getArg('port') || ((_c = config.webos) === null || _c === void 0 ? void 0 : _c.port) || ((_d = config.common) === null || _d === void 0 ? void 0 : _d.devServerPort) || 3000;
33
- const appUrl = getArg('url') ||
34
- ((_e = config.webos) === null || _e === void 0 ? void 0 : _e.url) ||
35
- `http://${(0, ip_1.getLocalIp)()}:${port}`;
34
+ const port = getArg('port') ||
35
+ ((_d = config.webos) === null || _d === void 0 ? void 0 : _d.port) ||
36
+ ((_e = config.common) === null || _e === void 0 ? void 0 : _e.devServerPort) ||
37
+ 3000;
38
+ const appUrl = getArg('url') || ((_f = config.webos) === null || _f === void 0 ? void 0 : _f.url) || `http://${(0, ip_1.getLocalIp)()}:${port}`;
36
39
  // 등록된 기기가 없는 경우
37
40
  if (!deviceName) {
38
41
  console.log('\n📺 연결된 WebOS TV 정보가 없습니다.');
@@ -70,8 +73,6 @@ function runWebOS() {
70
73
  if (fs_extra_1.default.existsSync(tempAppDir))
71
74
  fs_extra_1.default.removeSync(tempAppDir);
72
75
  fs_extra_1.default.ensureDirSync(tempAppDir);
73
- // appinfo.json 생성
74
- yield ensureWebosAppInfo(appId, tempAppDir);
75
76
  // 콘텐츠 모드 설정
76
77
  if (targetArg) {
77
78
  const distPath = path_1.default.resolve(process.cwd(), targetArg);
@@ -99,14 +100,21 @@ function runWebOS() {
99
100
  `;
100
101
  fs_extra_1.default.writeFileSync(path_1.default.join(tempAppDir, 'index.html'), redirectHtml);
101
102
  }
103
+ // appinfo.json 생성 (우선순위: dist > 지정경로 > 루트 > 기본값)
104
+ yield ensureWebosAppInfo(appId, tempAppDir, appInfoPath);
105
+ // 실제 appId를 appinfo.json에서 읽음 (dist에 있던 값 우선)
106
+ const resolvedAppInfoPath = path_1.default.join(tempAppDir, 'appinfo.json');
107
+ const resolvedAppId = fs_extra_1.default.existsSync(resolvedAppInfoPath)
108
+ ? fs_extra_1.default.readJsonSync(resolvedAppInfoPath).id || appId
109
+ : appId;
102
110
  // 아이콘
103
111
  (0, platform_1.resolveIcon)(tempAppDir);
104
112
  // 패키징
105
- console.log(`🔨 Packaging [${appId}]...`);
113
+ console.log(`🔨 Packaging [${resolvedAppId}]...`);
106
114
  (0, child_process_1.execSync)(`ares-package ${tempAppDir} -o .`, { stdio: 'inherit' });
107
115
  const ipkFile = fs_extra_1.default
108
116
  .readdirSync(process.cwd())
109
- .find((f) => f.startsWith(appId) && f.endsWith('.ipk'));
117
+ .find((f) => f.startsWith(resolvedAppId) && f.endsWith('.ipk'));
110
118
  if (ipkFile) {
111
119
  // 설치
112
120
  console.log(`🚀 Installing to ${deviceName}...`);
@@ -115,7 +123,9 @@ function runWebOS() {
115
123
  });
116
124
  // 런치
117
125
  console.log(`▶️ Launching App...`);
118
- (0, child_process_1.execSync)(`ares-launch -d ${deviceName} ${appId}`, { stdio: 'inherit' });
126
+ (0, child_process_1.execSync)(`ares-launch -d ${deviceName} ${resolvedAppId}`, {
127
+ stdio: 'inherit',
128
+ });
119
129
  // 파일 정리
120
130
  fs_extra_1.default.removeSync(path_1.default.resolve(process.cwd(), ipkFile));
121
131
  fs_extra_1.default.removeSync(tempAppDir);
@@ -127,26 +137,31 @@ function runWebOS() {
127
137
  }
128
138
  });
129
139
  }
130
- // appinfo.json이 없으면 자동 생성
131
- function ensureWebosAppInfo(appId, tempDir) {
140
+ // appinfo.json이 없으면 자동 생성 (우선순위: dist > 지정경로 > 루트 > 기본값)
141
+ function ensureWebosAppInfo(appId, tempDir, specifiedPath) {
132
142
  return __awaiter(this, void 0, void 0, function* () {
133
- const appInfoPath = path_1.default.join(tempDir, 'appinfo.json');
134
- const rootAppInfo = path_1.default.resolve(process.cwd(), 'appinfo.json');
135
- if (fs_extra_1.default.existsSync(rootAppInfo)) {
136
- fs_extra_1.default.copySync(rootAppInfo, appInfoPath);
137
- }
138
- else {
139
- const defaultAppInfo = {
140
- id: appId,
141
- version: '1.0.0',
142
- vendor: 'CtvRun',
143
- type: 'web',
144
- main: 'index.html',
145
- title: 'CTV-Run-App',
146
- icon: 'icon.png',
147
- uiRevision: 2,
148
- };
149
- fs_extra_1.default.writeJsonSync(appInfoPath, defaultAppInfo, { spaces: 2 });
143
+ const destPath = path_1.default.join(tempDir, 'appinfo.json');
144
+ if (fs_extra_1.default.existsSync(destPath))
145
+ return;
146
+ const candidates = [
147
+ specifiedPath && path_1.default.resolve(process.cwd(), specifiedPath),
148
+ path_1.default.resolve(process.cwd(), 'appinfo.json'),
149
+ ].filter(Boolean);
150
+ for (const candidate of candidates) {
151
+ if (fs_extra_1.default.existsSync(candidate)) {
152
+ fs_extra_1.default.copySync(candidate, destPath);
153
+ return;
154
+ }
150
155
  }
156
+ fs_extra_1.default.writeJsonSync(destPath, {
157
+ id: appId,
158
+ version: '1.0.0',
159
+ vendor: 'CtvRun',
160
+ type: 'web',
161
+ main: 'index.html',
162
+ title: 'CTV-Run-App',
163
+ icon: 'icon.png',
164
+ uiRevision: 2,
165
+ }, { spaces: 2 });
151
166
  });
152
167
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zsukim/ctv-run",
3
- "version": "1.0.11",
3
+ "version": "1.0.13",
4
4
  "description": "Smart TV deployment CLI for Vizio, LG webOS, Fire TV, and Samsung Tizen",
5
5
  "keywords": [
6
6
  "ctv",