@zsukim/ctv-run 1.0.10 → 1.0.12
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 +7 -5
- package/dist/icon.png +0 -0
- package/dist/platforms/tizen.js +100 -58
- package/dist/platforms/webos.js +10 -5
- package/dist/utils/platform.js +13 -2
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -18,14 +18,16 @@
|
|
|
18
18
|
|
|
19
19
|
## 실행 모드 개요
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
플랫폼은 두 가지 모드를 지원합니다.
|
|
22
22
|
|
|
23
|
-
| 모드 | 옵션 | 설명
|
|
24
|
-
|
|
25
|
-
| **Live Mode** | (기본값) | TV가 로드할 URL을 TV에 주입. HMR 지원 |
|
|
26
|
-
| **Static Mode** | `--target=dist` | 빌드 결과물을 TV에 직접 설치 |
|
|
23
|
+
| 모드 | 옵션 | 설명 |
|
|
24
|
+
|------|------|----------------------------------------|
|
|
25
|
+
| **Live Mode** | (기본값) | TV가 로드할 URL을 TV에 주입. HMR 지원 (Tizen 제외) |
|
|
26
|
+
| **Static Mode** | `--target=dist` | 빌드 결과물을 TV에 직접 설치 (Vizio 제외) |
|
|
27
27
|
|
|
28
28
|
> **Vizio**는 구조상 항상 URL이 필요하므로 Live Mode만 지원합니다.
|
|
29
|
+
>
|
|
30
|
+
> **Tizen** Live Mode는 HMR을 지원하지 않습니다. Samsung Tizen TV의 구형 WebKit이 ES native modules(`type="module"`)를 지원하지 않아 `vite dev` 서버에 접속하면 검은 화면이 나타납니다. `vite preview --host`를 사용하세요.
|
|
29
31
|
|
|
30
32
|
---
|
|
31
33
|
|
package/dist/icon.png
ADDED
|
Binary file
|
package/dist/platforms/tizen.js
CHANGED
|
@@ -30,14 +30,30 @@ function runTizen() {
|
|
|
30
30
|
console.error('\n❌ Tizen TV IP 주소가 필요합니다! (예: --ip=192.168.x.x)');
|
|
31
31
|
return;
|
|
32
32
|
}
|
|
33
|
-
const
|
|
34
|
-
|
|
33
|
+
const resolveBin = (name, fallbackPaths) => {
|
|
34
|
+
try {
|
|
35
|
+
(0, child_process_1.execSync)(`which ${name}`, { stdio: 'ignore' });
|
|
36
|
+
return name;
|
|
37
|
+
}
|
|
38
|
+
catch (_a) {
|
|
39
|
+
return fallbackPaths.find((p) => fs_extra_1.default.existsSync(p)) || name;
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
const tizenBin = getArg('tizenPath') ||
|
|
43
|
+
((_b = config.tizen) === null || _b === void 0 ? void 0 : _b.tizenPath) ||
|
|
44
|
+
resolveBin('tizen', [
|
|
45
|
+
`${process.env.HOME}/tizen-studio/tools/ide/bin/tizen`,
|
|
46
|
+
]);
|
|
47
|
+
const sdbBin = getArg('sdbPath') ||
|
|
48
|
+
((_c = config.tizen) === null || _c === void 0 ? void 0 : _c.sdbPath) ||
|
|
49
|
+
resolveBin('sdb', [`${process.env.HOME}/tizen-studio/tools/sdb`]);
|
|
35
50
|
const targetArg = getArg('target');
|
|
36
51
|
const isLiveMode = !targetArg;
|
|
37
|
-
const port = getArg('port') ||
|
|
38
|
-
|
|
39
|
-
((
|
|
40
|
-
|
|
52
|
+
const port = getArg('port') ||
|
|
53
|
+
((_d = config.tizen) === null || _d === void 0 ? void 0 : _d.port) ||
|
|
54
|
+
((_e = config.common) === null || _e === void 0 ? void 0 : _e.devServerPort) ||
|
|
55
|
+
3000;
|
|
56
|
+
const appUrl = getArg('url') || ((_f = config.tizen) === null || _f === void 0 ? void 0 : _f.url) || `http://${(0, ip_1.getLocalIp)()}:${port}`;
|
|
41
57
|
const tempAppDir = path_1.default.resolve(process.cwd(), '.ctv-run-tizen');
|
|
42
58
|
if (fs_extra_1.default.existsSync(tempAppDir))
|
|
43
59
|
fs_extra_1.default.removeSync(tempAppDir);
|
|
@@ -46,16 +62,8 @@ function runTizen() {
|
|
|
46
62
|
console.log(`🚀 [${isLiveMode ? 'Live Mode' : 'Static Mode'}] 준비 중...`);
|
|
47
63
|
// 콘텐츠 준비 및 기존 config.xml 탐색
|
|
48
64
|
if (isLiveMode) {
|
|
49
|
-
// Live Mode:
|
|
50
|
-
|
|
51
|
-
path_1.default.resolve(process.cwd(), 'config.xml'),
|
|
52
|
-
path_1.default.resolve(process.cwd(), 'public', 'config.xml'),
|
|
53
|
-
];
|
|
54
|
-
const foundPath = searchPaths.find((p) => fs_extra_1.default.existsSync(p));
|
|
55
|
-
if (foundPath)
|
|
56
|
-
fs_extra_1.default.copySync(foundPath, path_1.default.join(tempAppDir, 'config.xml'));
|
|
57
|
-
const redirectHtml = `<!DOCTYPE html><html><head><meta charset="utf-8"></head><body style="background:#000;"><script type="text/javascript">window.location.replace("${appUrl}");</script></body></html>`;
|
|
58
|
-
fs_extra_1.default.writeFileSync(path_1.default.join(tempAppDir, 'index.html'), redirectHtml);
|
|
65
|
+
// Live Mode: index.html은 TV가 content src URL로 바로 접속하므로 불필요
|
|
66
|
+
// config.xml의 content src에 개발서버 URL을 직접 지정
|
|
59
67
|
}
|
|
60
68
|
else {
|
|
61
69
|
// Static Mode: 빌드 타겟 폴더 복사
|
|
@@ -73,56 +81,81 @@ function runTizen() {
|
|
|
73
81
|
((_g = config.tizen) === null || _g === void 0 ? void 0 : _g.appId) ||
|
|
74
82
|
'A123456789.CtvRunApp';
|
|
75
83
|
const safeAppName = ((xmlInfo === null || xmlInfo === void 0 ? void 0 : xmlInfo.appName) || 'CtvRunApp').replace(/\s+/g, '');
|
|
76
|
-
// config.xml
|
|
77
|
-
yield ensureTizenFiles(appId, tempAppDir, safeAppName);
|
|
84
|
+
// static mode는 dist의 config.xml 유지, live mode는 개발서버 URL로 새로 생성
|
|
85
|
+
yield ensureTizenFiles(appId, tempAppDir, safeAppName, !isLiveMode, isLiveMode ? appUrl : '');
|
|
78
86
|
// Tizen 인증서 프로필 확인/생성/활성화
|
|
79
87
|
console.log('\n🎫 Tizen 인증서 프로필 확인 중...');
|
|
80
|
-
const
|
|
88
|
+
const profilesXmlPath = path_1.default.join(process.env.HOME || '', 'tizen-studio-data/profile/profiles.xml');
|
|
89
|
+
const readProfilesXml = () => {
|
|
81
90
|
try {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const
|
|
86
|
-
|
|
91
|
+
if (!fs_extra_1.default.existsSync(profilesXmlPath))
|
|
92
|
+
return { active: '', profiles: [] };
|
|
93
|
+
const xml = fs_extra_1.default.readFileSync(profilesXmlPath, 'utf8');
|
|
94
|
+
const activeMatch = xml.match(/<profiles[^>]*active="([^"]+)"/);
|
|
95
|
+
const profileMatches = [...xml.matchAll(/<profile\s+name="([^"]+)"/g)];
|
|
96
|
+
return {
|
|
97
|
+
active: activeMatch ? activeMatch[1] : '',
|
|
98
|
+
profiles: profileMatches.map((m) => m[1]),
|
|
99
|
+
};
|
|
87
100
|
}
|
|
88
101
|
catch (e) {
|
|
89
|
-
return '';
|
|
102
|
+
return { active: '', profiles: [] };
|
|
90
103
|
}
|
|
91
104
|
};
|
|
92
|
-
|
|
105
|
+
const setActiveProfile = (name) => {
|
|
106
|
+
(0, child_process_1.execSync)(`${tizenBin} security-profiles set-active -n "${name}"`, {
|
|
107
|
+
stdio: 'inherit',
|
|
108
|
+
});
|
|
109
|
+
};
|
|
110
|
+
let activeProfile = readProfilesXml().active;
|
|
93
111
|
if (!activeProfile) {
|
|
94
|
-
console.log('⚠️ 활성화된 프로필이 없습니다. 앱 전용 인증서를 세팅합니다.');
|
|
95
112
|
const askQuestion = (query) => {
|
|
96
113
|
const buffer = Buffer.alloc(1024);
|
|
97
114
|
process.stdout.write(query);
|
|
98
115
|
const bytesRead = fs_extra_1.default.readSync(0, buffer, 0, 1024, null);
|
|
99
116
|
return buffer.toString('utf8', 0, bytesRead).trim();
|
|
100
117
|
};
|
|
101
|
-
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
if (fs_extra_1.default.existsSync(p12Path)) {
|
|
108
|
-
console.log(`✨ 기존 인증서 발견: ${p12Path}`);
|
|
109
|
-
p12Pwd = askQuestion('🔑 인증서 비밀번호를 입력하세요: ');
|
|
110
|
-
}
|
|
111
|
-
else {
|
|
112
|
-
p12Pwd = askQuestion('🔑 새로 만들 인증서 비밀번호(4자 이상): ');
|
|
113
|
-
(0, child_process_1.execSync)(`${tizenBin} certificate -a "${certFileName}" -p "${p12Pwd}" -f "${certFileName}"`, { stdio: 'inherit' });
|
|
114
|
-
}
|
|
115
|
-
try {
|
|
116
|
-
(0, child_process_1.execSync)(`${tizenBin} security-profiles remove -n "${profileName}"`, {
|
|
117
|
-
stdio: 'ignore',
|
|
118
|
+
// 등록된 프로필 목록 확인
|
|
119
|
+
const existingProfiles = readProfilesXml().profiles;
|
|
120
|
+
if (existingProfiles.length > 0) {
|
|
121
|
+
console.log('⚠️ 활성화된 프로필이 없습니다. 등록된 프로필 목록:');
|
|
122
|
+
existingProfiles.forEach((p, i) => {
|
|
123
|
+
console.log(` ${i + 1}. ${p}`);
|
|
118
124
|
});
|
|
125
|
+
const answer = askQuestion('🔑 사용할 프로필 번호를 입력하세요 (새로운 ctv-run-cert 생성): ');
|
|
126
|
+
const idx = parseInt(answer, 10) - 1;
|
|
127
|
+
if (!isNaN(idx) && idx >= 0 && idx < existingProfiles.length) {
|
|
128
|
+
const chosen = existingProfiles[idx];
|
|
129
|
+
setActiveProfile(chosen);
|
|
130
|
+
activeProfile = readProfilesXml().active;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (!activeProfile) {
|
|
134
|
+
console.log('⚠️ 활성화된 프로필이 없습니다. 앱 전용 인증서를 세팅합니다.');
|
|
135
|
+
const certFileName = 'ctv-run-cert';
|
|
136
|
+
const profileName = 'ctv-run-profile';
|
|
137
|
+
const homeDir = process.env.HOME || '';
|
|
138
|
+
const defaultCertDir = path_1.default.join(homeDir, 'tizen-studio-data/keystore/author');
|
|
139
|
+
const p12Path = path_1.default.join(defaultCertDir, `${certFileName}.p12`);
|
|
140
|
+
let p12Pwd = '';
|
|
141
|
+
if (fs_extra_1.default.existsSync(p12Path)) {
|
|
142
|
+
console.log(`✨ 기존 인증서 발견: ${p12Path}`);
|
|
143
|
+
p12Pwd = askQuestion('🔑 인증서 비밀번호를 입력하세요: ');
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
p12Pwd = askQuestion('🔑 새로 만들 인증서 비밀번호(4자 이상): ');
|
|
147
|
+
(0, child_process_1.execSync)(`${tizenBin} certificate -a "${certFileName}" -p "${p12Pwd}" -f "${certFileName}"`, { stdio: 'inherit' });
|
|
148
|
+
}
|
|
149
|
+
try {
|
|
150
|
+
(0, child_process_1.execSync)(`${tizenBin} security-profiles remove -n "${profileName}"`, {
|
|
151
|
+
stdio: 'ignore',
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
catch (e) { }
|
|
155
|
+
(0, child_process_1.execSync)(`${tizenBin} security-profiles add -n "${profileName}" -a "${p12Path}" -p "${p12Pwd}"`, { stdio: 'inherit' });
|
|
156
|
+
setActiveProfile(profileName);
|
|
157
|
+
activeProfile = readProfilesXml().active;
|
|
119
158
|
}
|
|
120
|
-
catch (e) { }
|
|
121
|
-
(0, child_process_1.execSync)(`${tizenBin} security-profiles add -n "${profileName}" -a "${p12Path}" -p "${p12Pwd}"`, { stdio: 'inherit' });
|
|
122
|
-
(0, child_process_1.execSync)(`${tizenBin} security-profiles set-active -n "${profileName}"`, {
|
|
123
|
-
stdio: 'inherit',
|
|
124
|
-
});
|
|
125
|
-
activeProfile = getActive();
|
|
126
159
|
}
|
|
127
160
|
// 패키징
|
|
128
161
|
console.log(`\n🔨 [${activeProfile}] 프로필로 패키징 중...`);
|
|
@@ -151,7 +184,7 @@ function runTizen() {
|
|
|
151
184
|
return buffer.toString('utf8', 0, bytesRead).trim().toLowerCase();
|
|
152
185
|
};
|
|
153
186
|
const answer = askQuestion('❓ 기존 앱을 삭제하고 다시 설치할까요? (Y/n): ');
|
|
154
|
-
if (answer === '
|
|
187
|
+
if (answer === 'y') {
|
|
155
188
|
console.log(`🗑️ 기존 앱 삭제 중: ${appId}`);
|
|
156
189
|
(0, child_process_1.execSync)(`${tizenBin} uninstall -p ${appId}`, { stdio: 'inherit' });
|
|
157
190
|
console.log('✅ 삭제 완료. 재설치 시도...');
|
|
@@ -198,10 +231,15 @@ function getInfoFromXml(dir) {
|
|
|
198
231
|
}
|
|
199
232
|
return null;
|
|
200
233
|
}
|
|
201
|
-
function ensureTizenFiles(
|
|
202
|
-
return __awaiter(this,
|
|
234
|
+
function ensureTizenFiles(appId_1, tempDir_1, appName_1) {
|
|
235
|
+
return __awaiter(this, arguments, void 0, function* (appId, tempDir, appName, keepExistingConfig = false, liveUrl = '') {
|
|
203
236
|
const configXmlPath = path_1.default.join(tempDir, 'config.xml');
|
|
204
|
-
|
|
237
|
+
if (keepExistingConfig && fs_extra_1.default.existsSync(configXmlPath)) {
|
|
238
|
+
// static mode: dist의 config.xml을 그대로 유지
|
|
239
|
+
(0, platform_1.resolveIcon)(tempDir);
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
// live mode: 기존 config.xml 삭제 후 새로 생성
|
|
205
243
|
if (fs_extra_1.default.existsSync(configXmlPath)) {
|
|
206
244
|
fs_extra_1.default.removeSync(configXmlPath);
|
|
207
245
|
}
|
|
@@ -210,19 +248,23 @@ function ensureTizenFiles(appId, tempDir, appName) {
|
|
|
210
248
|
const packageId = appId.split('.')[0] || 'TizenApp';
|
|
211
249
|
const xmlContent = `<?xml version="1.0" encoding="UTF-8"?>
|
|
212
250
|
<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">
|
|
213
|
-
<tizen:application id="${appId}" package="${packageId}" required_version="2.
|
|
214
|
-
<tizen:profile name="tv-samsung"/>
|
|
251
|
+
<tizen:application id="${appId}" package="${packageId}" required_version="2.1"/>
|
|
215
252
|
<content src="index.html"/>
|
|
216
|
-
<name
|
|
253
|
+
<feature name="http://tizen.org/feature/screen.size.normal.1080.1920"/>
|
|
217
254
|
<icon src="icon.png"/>
|
|
255
|
+
<name>${appName}</name>
|
|
256
|
+
<tizen:profile name="tv-samsung"/>
|
|
218
257
|
<access origin="*" subdomains="true"/>
|
|
219
|
-
<tizen:allow-navigation>*</tizen:allow-navigation>
|
|
220
258
|
<tizen:privilege name="http://tizen.org/privilege/internet"/>
|
|
221
259
|
<tizen:privilege name="http://developer.samsung.com/privilege/network.public"/>
|
|
222
260
|
<tizen:privilege name="http://tizen.org/privilege/tv.inputdevice" />
|
|
223
261
|
<tizen:setting screen-orientation="landscape" context-menu="enable" background-support="disable" encryption="disable" install-location="auto" hwkey-event="enable"/>
|
|
224
262
|
</widget>`;
|
|
225
263
|
fs_extra_1.default.writeFileSync(configXmlPath, xmlContent, 'utf8');
|
|
264
|
+
if (liveUrl) {
|
|
265
|
+
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>`;
|
|
266
|
+
fs_extra_1.default.writeFileSync(path_1.default.join(tempDir, 'index.html'), redirectHtml);
|
|
267
|
+
}
|
|
226
268
|
(0, platform_1.resolveIcon)(tempDir);
|
|
227
269
|
});
|
|
228
270
|
}
|
package/dist/platforms/webos.js
CHANGED
|
@@ -70,8 +70,6 @@ function runWebOS() {
|
|
|
70
70
|
if (fs_extra_1.default.existsSync(tempAppDir))
|
|
71
71
|
fs_extra_1.default.removeSync(tempAppDir);
|
|
72
72
|
fs_extra_1.default.ensureDirSync(tempAppDir);
|
|
73
|
-
// appinfo.json 생성
|
|
74
|
-
yield ensureWebosAppInfo(appId, tempAppDir);
|
|
75
73
|
// 콘텐츠 모드 설정
|
|
76
74
|
if (targetArg) {
|
|
77
75
|
const distPath = path_1.default.resolve(process.cwd(), targetArg);
|
|
@@ -99,14 +97,21 @@ function runWebOS() {
|
|
|
99
97
|
`;
|
|
100
98
|
fs_extra_1.default.writeFileSync(path_1.default.join(tempAppDir, 'index.html'), redirectHtml);
|
|
101
99
|
}
|
|
100
|
+
// appinfo.json 생성 (dist에 없으면 기본값으로 생성)
|
|
101
|
+
yield ensureWebosAppInfo(appId, tempAppDir);
|
|
102
|
+
// 실제 appId를 appinfo.json에서 읽음 (dist에 있던 값 우선)
|
|
103
|
+
const appInfoPath = path_1.default.join(tempAppDir, 'appinfo.json');
|
|
104
|
+
const resolvedAppId = fs_extra_1.default.existsSync(appInfoPath)
|
|
105
|
+
? fs_extra_1.default.readJsonSync(appInfoPath).id || appId
|
|
106
|
+
: appId;
|
|
102
107
|
// 아이콘
|
|
103
108
|
(0, platform_1.resolveIcon)(tempAppDir);
|
|
104
109
|
// 패키징
|
|
105
|
-
console.log(`🔨 Packaging [${
|
|
110
|
+
console.log(`🔨 Packaging [${resolvedAppId}]...`);
|
|
106
111
|
(0, child_process_1.execSync)(`ares-package ${tempAppDir} -o .`, { stdio: 'inherit' });
|
|
107
112
|
const ipkFile = fs_extra_1.default
|
|
108
113
|
.readdirSync(process.cwd())
|
|
109
|
-
.find((f) => f.startsWith(
|
|
114
|
+
.find((f) => f.startsWith(resolvedAppId) && f.endsWith('.ipk'));
|
|
110
115
|
if (ipkFile) {
|
|
111
116
|
// 설치
|
|
112
117
|
console.log(`🚀 Installing to ${deviceName}...`);
|
|
@@ -115,7 +120,7 @@ function runWebOS() {
|
|
|
115
120
|
});
|
|
116
121
|
// 런치
|
|
117
122
|
console.log(`▶️ Launching App...`);
|
|
118
|
-
(0, child_process_1.execSync)(`ares-launch -d ${deviceName} ${
|
|
123
|
+
(0, child_process_1.execSync)(`ares-launch -d ${deviceName} ${resolvedAppId}`, { stdio: 'inherit' });
|
|
119
124
|
// 파일 정리
|
|
120
125
|
fs_extra_1.default.removeSync(path_1.default.resolve(process.cwd(), ipkFile));
|
|
121
126
|
fs_extra_1.default.removeSync(tempAppDir);
|
package/dist/utils/platform.js
CHANGED
|
@@ -12,15 +12,26 @@ function makeArgParser(args) {
|
|
|
12
12
|
}
|
|
13
13
|
function resolveIcon(tempDir) {
|
|
14
14
|
const targetIconPath = path_1.default.join(tempDir, 'icon.png');
|
|
15
|
-
const
|
|
15
|
+
const iconSearchPaths = [
|
|
16
|
+
path_1.default.resolve(process.cwd(), 'src', 'assets', 'icon.png'),
|
|
17
|
+
path_1.default.resolve(process.cwd(), 'public', 'icon.png'),
|
|
18
|
+
path_1.default.resolve(process.cwd(), 'icon.png'),
|
|
19
|
+
path_1.default.resolve(process.cwd(), 'assets', 'icon.png'),
|
|
20
|
+
];
|
|
21
|
+
const sourceIconPath = iconSearchPaths.find((p) => fs_extra_1.default.existsSync(p));
|
|
16
22
|
if (fs_extra_1.default.existsSync(targetIconPath)) {
|
|
17
23
|
console.log('✅ 기존 프로젝트의 아이콘을 유지합니다: icon.png');
|
|
18
24
|
return;
|
|
19
25
|
}
|
|
20
|
-
|
|
26
|
+
const fallbackIconPath = path_1.default.join(__dirname, '../icon.png');
|
|
27
|
+
if (sourceIconPath) {
|
|
21
28
|
fs_extra_1.default.copySync(sourceIconPath, targetIconPath);
|
|
22
29
|
console.log('🖼 기본 아이콘을 가져왔습니다.');
|
|
23
30
|
}
|
|
31
|
+
else if (fs_extra_1.default.existsSync(fallbackIconPath)) {
|
|
32
|
+
fs_extra_1.default.copySync(fallbackIconPath, targetIconPath);
|
|
33
|
+
console.log('🖼 ctv-run 기본 아이콘을 사용합니다.');
|
|
34
|
+
}
|
|
24
35
|
else {
|
|
25
36
|
const dummyPng = Buffer.from('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=', 'base64');
|
|
26
37
|
fs_extra_1.default.writeFileSync(targetIconPath, dummyPng);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zsukim/ctv-run",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.12",
|
|
4
4
|
"description": "Smart TV deployment CLI for Vizio, LG webOS, Fire TV, and Samsung Tizen",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ctv",
|
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
"node": ">=22"
|
|
27
27
|
},
|
|
28
28
|
"scripts": {
|
|
29
|
-
"build": "tsc",
|
|
29
|
+
"build": "tsc && node -e \"require('fs-extra').copySync('src/assets/icon.png', 'dist/icon.png')\"",
|
|
30
30
|
"watch": "tsc -w",
|
|
31
|
-
"prepublishOnly": "tsc",
|
|
31
|
+
"prepublishOnly": "tsc && node -e \"require('fs-extra').copySync('src/assets/icon.png', 'dist/icon.png')\"",
|
|
32
32
|
"release:patch": "npm version patch && git push && git push --tags",
|
|
33
33
|
"release:minor": "npm version minor && git push && git push --tags",
|
|
34
34
|
"release:major": "npm version major && git push && git push --tags"
|