creative-upload-extension-updater 0.1.4 → 0.1.6
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 +12 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +110 -0
- package/dist/cli.js.map +1 -0
- package/dist/native-host.d.ts +2 -0
- package/dist/native-host.js +31 -0
- package/dist/native-host.js.map +1 -0
- package/dist/updater.d.ts +1 -0
- package/dist/updater.js +205 -0
- package/dist/updater.js.map +1 -0
- package/package.json +14 -2
- package/bin/creative-upload-extension-updater.js +0 -110
- package/bin/native-host.js +0 -29
- package/lib/updater.js +0 -190
package/README.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
macOS Native Messaging updater for the unpacked Creative Upload Chrome extension.
|
|
4
4
|
|
|
5
|
+
## Development
|
|
6
|
+
|
|
7
|
+
The CLI is implemented in TypeScript under `src/` and compiled into `dist/`.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install
|
|
11
|
+
npm run typecheck
|
|
12
|
+
npm run build
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
`npm pack` and `npm publish` run the build automatically through `prepack`.
|
|
16
|
+
|
|
5
17
|
## First installation
|
|
6
18
|
|
|
7
19
|
```bash
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const fs = require('node:fs');
|
|
5
|
+
const path = require('node:path');
|
|
6
|
+
const { diagnose, install, requestChromeReload, rollback, uninstall, update, TRANSITION_HTTP_REGISTRY_ORIGIN } = require('./updater');
|
|
7
|
+
const packageVersion = require('../package.json').version;
|
|
8
|
+
const printJson = (value) => process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
|
|
9
|
+
const printResult = (args, value, lines) => {
|
|
10
|
+
if (args.json === true)
|
|
11
|
+
return printJson(value);
|
|
12
|
+
process.stdout.write(`${lines.join('\n')}\n`);
|
|
13
|
+
};
|
|
14
|
+
const printInstallGuide = (extensionDirectory) => {
|
|
15
|
+
process.stdout.write('\n安装完成。请在 Chrome 完成一次加载:\n\n');
|
|
16
|
+
process.stdout.write(' 1. 打开 chrome://extensions\n');
|
|
17
|
+
process.stdout.write(' 2. 开启右上角「开发者模式」\n');
|
|
18
|
+
process.stdout.write(' 3. 点击「加载已解压的扩展程序」\n');
|
|
19
|
+
process.stdout.write(` 4. 在文件选择器中按 Command + Shift + G,粘贴并打开:${extensionDirectory}\n\n`);
|
|
20
|
+
process.stdout.write('后续发布新版本后,扩展会定时检查更新;也可手动运行 creative-upload-updater update。\n');
|
|
21
|
+
};
|
|
22
|
+
const usage = `用法:creative-upload-updater <命令> [选项]
|
|
23
|
+
|
|
24
|
+
命令:
|
|
25
|
+
install [--registry <url>] [--allow-insecure] 安装更新器与首次插件版本
|
|
26
|
+
update [--no-reload] 下载并应用最新插件版本
|
|
27
|
+
diagnose 输出本机安装诊断
|
|
28
|
+
rollback 回滚到上一插件版本
|
|
29
|
+
uninstall 移除更新器注册、插件文件与历史版本
|
|
30
|
+
|
|
31
|
+
选项:
|
|
32
|
+
--help, -h 显示帮助
|
|
33
|
+
--version, -v 显示版本
|
|
34
|
+
--json 以 JSON 输出命令结果`;
|
|
35
|
+
const parseArguments = (values) => values.reduce((result, value, index) => {
|
|
36
|
+
if (!value.startsWith('--'))
|
|
37
|
+
return result;
|
|
38
|
+
const key = value.slice(2);
|
|
39
|
+
const next = values[index + 1];
|
|
40
|
+
result[key] = next && !next.startsWith('--') ? next : true;
|
|
41
|
+
return result;
|
|
42
|
+
}, {});
|
|
43
|
+
const main = async () => {
|
|
44
|
+
const [command] = process.argv.slice(2);
|
|
45
|
+
const args = parseArguments(process.argv.slice(3));
|
|
46
|
+
if (!command || command === '--help' || command === '-h' || args.help === true) {
|
|
47
|
+
process.stdout.write(`${usage}\n`);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (command === '--version' || command === '-v' || args.version === true) {
|
|
51
|
+
process.stdout.write(`${packageVersion}\n`);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (command === 'install') {
|
|
55
|
+
const publicKey = typeof args['public-key'] === 'string'
|
|
56
|
+
? fs.readFileSync(path.resolve(args['public-key']), 'utf8')
|
|
57
|
+
: undefined;
|
|
58
|
+
const result = await install({
|
|
59
|
+
registryUrl: typeof args.registry === 'string' ? args.registry : TRANSITION_HTTP_REGISTRY_ORIGIN,
|
|
60
|
+
extensionId: typeof args['extension-id'] === 'string' ? args['extension-id'] : undefined,
|
|
61
|
+
publicKey,
|
|
62
|
+
allowInsecure: args['allow-insecure'] === true,
|
|
63
|
+
hostPath: path.resolve(__dirname, 'native-host.js'),
|
|
64
|
+
});
|
|
65
|
+
printResult(args, result, [
|
|
66
|
+
'插件更新器安装完成。',
|
|
67
|
+
`已安装插件版本:${result.targetVersion ?? result.currentVersion}`,
|
|
68
|
+
`扩展目录:${result.extensionDirectory}`,
|
|
69
|
+
]);
|
|
70
|
+
if (args.json !== true)
|
|
71
|
+
printInstallGuide(result.extensionDirectory);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (command === 'diagnose') {
|
|
75
|
+
const result = diagnose();
|
|
76
|
+
return printResult(args, result, [
|
|
77
|
+
'更新器诊断结果:',
|
|
78
|
+
`扩展目录:${result.extensionDirectory}`,
|
|
79
|
+
`扩展文件:${result.extensionLoaded ? '已找到' : '未找到'}`,
|
|
80
|
+
`Native Messaging:${result.nativeHostRegistered ? '已注册' : '未注册'}`,
|
|
81
|
+
`更新源:${result.registryUrl}`,
|
|
82
|
+
`扩展 ID:${result.extensionId}`,
|
|
83
|
+
]);
|
|
84
|
+
}
|
|
85
|
+
if (command === 'update') {
|
|
86
|
+
const result = await update();
|
|
87
|
+
if (result.status === 'updated' && args['no-reload'] !== true) {
|
|
88
|
+
const diagnostic = diagnose();
|
|
89
|
+
Object.assign(result, { chromeReload: requestChromeReload(diagnostic.extensionId) });
|
|
90
|
+
}
|
|
91
|
+
const changed = result.status === 'updated';
|
|
92
|
+
return printResult(args, result, changed
|
|
93
|
+
? [`更新完成:${result.currentVersion} → ${result.targetVersion}`, 'Chrome 将自动重新加载扩展;如未生效,请在 chrome://extensions 手动重新加载。']
|
|
94
|
+
: [`已是最新版本:${result.currentVersion}`]);
|
|
95
|
+
}
|
|
96
|
+
if (command === 'rollback') {
|
|
97
|
+
const result = rollback();
|
|
98
|
+
return printResult(args, result, ['已回滚到上一插件版本。', `扩展目录:${result.extensionDirectory}`, '请在 chrome://extensions 手动重新加载扩展。']);
|
|
99
|
+
}
|
|
100
|
+
if (command === 'uninstall') {
|
|
101
|
+
const result = uninstall();
|
|
102
|
+
return printResult(args, result, ['卸载完成。', '已删除更新器注册、插件文件与历史版本。', '请在 chrome://extensions 手动移除已加载的扩展。']);
|
|
103
|
+
}
|
|
104
|
+
throw new Error(`未知命令:${command}\n\n${usage}`);
|
|
105
|
+
};
|
|
106
|
+
main().catch((error) => {
|
|
107
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
108
|
+
process.exitCode = 1;
|
|
109
|
+
});
|
|
110
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const { checkAndUpdate } = require('./updater');
|
|
5
|
+
let input = Buffer.alloc(0);
|
|
6
|
+
const respond = (value) => {
|
|
7
|
+
const body = Buffer.from(JSON.stringify(value));
|
|
8
|
+
const header = Buffer.alloc(4);
|
|
9
|
+
header.writeUInt32LE(body.length, 0);
|
|
10
|
+
process.stdout.write(Buffer.concat([header, body]));
|
|
11
|
+
};
|
|
12
|
+
process.stdin.on('data', (chunk) => {
|
|
13
|
+
input = Buffer.concat([input, chunk]);
|
|
14
|
+
if (input.length < 4)
|
|
15
|
+
return;
|
|
16
|
+
const length = input.readUInt32LE(0);
|
|
17
|
+
if (input.length < length + 4)
|
|
18
|
+
return;
|
|
19
|
+
const message = JSON.parse(input.subarray(4, length + 4).toString('utf8'));
|
|
20
|
+
if (message.type !== 'check_and_update')
|
|
21
|
+
return respond({ status: 'failed', message: '不支持的更新器请求。' });
|
|
22
|
+
void checkAndUpdate(message)
|
|
23
|
+
.then(respond)
|
|
24
|
+
.catch((error) => respond({
|
|
25
|
+
status: 'failed',
|
|
26
|
+
currentVersion: message.currentVersion,
|
|
27
|
+
checkedAt: Date.now(),
|
|
28
|
+
message: error instanceof Error ? error.message : '插件更新失败。',
|
|
29
|
+
}));
|
|
30
|
+
});
|
|
31
|
+
//# sourceMappingURL=native-host.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native-host.js","sourceRoot":"","sources":["../src/native-host.ts"],"names":[],"mappings":";;;AACA,MAAM,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;AAEhD,IAAI,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAC5B,MAAM,OAAO,GAAG,CAAC,KAAK,EAAE,EAAE;IACxB,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;IAChD,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC/B,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACrC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;AACtD,CAAC,CAAC;AACF,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;IACjC,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;IACtC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO;IAC7B,MAAM,MAAM,GAAG,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IACrC,IAAI,KAAK,CAAC,MAAM,GAAG,MAAM,GAAG,CAAC;QAAE,OAAO;IACtC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;IAC3E,IAAI,OAAO,CAAC,IAAI,KAAK,kBAAkB;QACrC,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC,CAAC;IAC9D,KAAK,cAAc,CAAC,OAAO,CAAC;SACzB,IAAI,CAAC,OAAO,CAAC;SACb,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CACf,OAAO,CAAC;QACN,MAAM,EAAE,QAAQ;QAChB,cAAc,EAAE,OAAO,CAAC,cAAc;QACtC,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;QACrB,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS;KAC5D,CAAC,CACH,CAAC;AACN,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/updater.js
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const crypto = require('node:crypto');
|
|
4
|
+
const fs = require('node:fs');
|
|
5
|
+
const os = require('node:os');
|
|
6
|
+
const path = require('node:path');
|
|
7
|
+
const { execFileSync } = require('node:child_process');
|
|
8
|
+
const HOST_NAME = 'com.bytedance.creative_upload_extension_updater';
|
|
9
|
+
const DEFAULT_EXTENSION_ID = 'amfhocfbdlbooohhoinhhhgobfdmaljc';
|
|
10
|
+
const TRANSITION_HTTP_REGISTRY_ORIGIN = 'http://10.37.83.141:3100';
|
|
11
|
+
const DEFAULT_PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
|
|
12
|
+
MCowBQYDK2VwAyEAlMYzNfVA3wLMfEniZDN3VQ+hfVXgMspwK6rM4h2P6Mk=
|
|
13
|
+
-----END PUBLIC KEY-----
|
|
14
|
+
`;
|
|
15
|
+
const baseDirectory = path.join(os.homedir(), 'Library', 'Application Support', 'CreativeUploadInspector');
|
|
16
|
+
const configPath = path.join(baseDirectory, 'updater.json');
|
|
17
|
+
const extensionDirectory = path.join(baseDirectory, 'extension');
|
|
18
|
+
const previousDirectory = path.join(baseDirectory, 'previous');
|
|
19
|
+
const nativeHostLauncherPath = path.join(baseDirectory, 'native-host');
|
|
20
|
+
const nativeHostManifestPath = path.join(os.homedir(), 'Library', 'Application Support', 'Google', 'Chrome', 'NativeMessagingHosts', `${HOST_NAME}.json`);
|
|
21
|
+
const readConfig = () => {
|
|
22
|
+
if (!fs.existsSync(configPath))
|
|
23
|
+
throw new Error('更新器尚未安装。请运行 creative-upload-extension-updater install。');
|
|
24
|
+
return JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
25
|
+
};
|
|
26
|
+
const writeConfig = (config) => {
|
|
27
|
+
fs.mkdirSync(baseDirectory, { recursive: true, mode: 0o700 });
|
|
28
|
+
fs.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`, { mode: 0o600 });
|
|
29
|
+
};
|
|
30
|
+
const assertUrl = (url, allowInsecure) => {
|
|
31
|
+
const parsed = new URL(url);
|
|
32
|
+
const transitionRegistry = parsed.origin === TRANSITION_HTTP_REGISTRY_ORIGIN;
|
|
33
|
+
if (parsed.protocol === 'http:' && !allowInsecure && !transitionRegistry) {
|
|
34
|
+
throw new Error(`仅允许 HTTPS 或当前过渡 Registry ${TRANSITION_HTTP_REGISTRY_ORIGIN}。其他 HTTP 地址需显式传入 --allow-insecure。`);
|
|
35
|
+
}
|
|
36
|
+
if (!['https:', 'http:'].includes(parsed.protocol)) {
|
|
37
|
+
throw new Error('更新源 URL 协议无效。');
|
|
38
|
+
}
|
|
39
|
+
return parsed;
|
|
40
|
+
};
|
|
41
|
+
const fetchJson = async (url, allowInsecure) => {
|
|
42
|
+
assertUrl(url, allowInsecure);
|
|
43
|
+
const response = await fetch(url);
|
|
44
|
+
if (!response.ok)
|
|
45
|
+
throw new Error(`读取更新元数据失败(HTTP ${response.status})。`);
|
|
46
|
+
return response.json();
|
|
47
|
+
};
|
|
48
|
+
const extensionIdFromKey = (key) => {
|
|
49
|
+
const hash = crypto.createHash('sha256').update(Buffer.from(key, 'base64')).digest('hex').slice(0, 32);
|
|
50
|
+
return hash.replace(/[0-9a-f]/g, (digit) => 'abcdefghijklmnop'[Number.parseInt(digit, 16)]);
|
|
51
|
+
};
|
|
52
|
+
const validateRelease = (release) => {
|
|
53
|
+
if (!release || typeof release !== 'object')
|
|
54
|
+
throw new Error('更新元数据格式无效。');
|
|
55
|
+
for (const field of ['version', 'artifactUrl', 'sha256', 'signature']) {
|
|
56
|
+
if (typeof release[field] !== 'string' || !release[field])
|
|
57
|
+
throw new Error(`更新元数据缺少 ${field}。`);
|
|
58
|
+
}
|
|
59
|
+
if (!/^[a-f0-9]{64}$/i.test(release.sha256))
|
|
60
|
+
throw new Error('更新元数据中的 SHA256 无效。');
|
|
61
|
+
};
|
|
62
|
+
const downloadAndVerify = async (release, config, staging) => {
|
|
63
|
+
assertUrl(release.artifactUrl, config.allowInsecure);
|
|
64
|
+
const response = await fetch(release.artifactUrl);
|
|
65
|
+
if (!response.ok)
|
|
66
|
+
throw new Error(`下载插件制品失败(HTTP ${response.status})。`);
|
|
67
|
+
const content = Buffer.from(await response.arrayBuffer());
|
|
68
|
+
const checksum = crypto.createHash('sha256').update(content).digest('hex');
|
|
69
|
+
if (!crypto.timingSafeEqual(Buffer.from(checksum), Buffer.from(release.sha256)))
|
|
70
|
+
throw new Error('插件制品 SHA256 校验失败。');
|
|
71
|
+
if (!crypto.verify(null, content, config.signingPublicKey, Buffer.from(release.signature, 'base64')))
|
|
72
|
+
throw new Error('插件制品签名校验失败。');
|
|
73
|
+
const archive = path.join(staging, 'extension.zip');
|
|
74
|
+
fs.writeFileSync(archive, content, { mode: 0o600 });
|
|
75
|
+
return archive;
|
|
76
|
+
};
|
|
77
|
+
const unpackAndValidate = (archive, staging, release, config) => {
|
|
78
|
+
const unpacked = path.join(staging, 'unpacked');
|
|
79
|
+
fs.mkdirSync(unpacked, { recursive: true, mode: 0o700 });
|
|
80
|
+
execFileSync('/usr/bin/ditto', ['-x', '-k', archive, unpacked], { stdio: 'pipe' });
|
|
81
|
+
const manifestPath = path.join(unpacked, 'manifest.json');
|
|
82
|
+
if (!fs.existsSync(manifestPath))
|
|
83
|
+
throw new Error('插件制品缺少 manifest.json。');
|
|
84
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
85
|
+
if (manifest.version !== release.version)
|
|
86
|
+
throw new Error('插件制品版本与发布元数据不一致。');
|
|
87
|
+
if (typeof manifest.key !== 'string' || extensionIdFromKey(manifest.key) !== config.extensionId)
|
|
88
|
+
throw new Error('插件制品的 Extension ID 不匹配。');
|
|
89
|
+
return unpacked;
|
|
90
|
+
};
|
|
91
|
+
const replaceExtension = (unpacked) => {
|
|
92
|
+
const failedDirectory = `${extensionDirectory}.failed-${Date.now()}`;
|
|
93
|
+
fs.rmSync(failedDirectory, { recursive: true, force: true });
|
|
94
|
+
try {
|
|
95
|
+
if (fs.existsSync(previousDirectory))
|
|
96
|
+
fs.rmSync(previousDirectory, { recursive: true, force: true });
|
|
97
|
+
if (fs.existsSync(extensionDirectory))
|
|
98
|
+
fs.renameSync(extensionDirectory, previousDirectory);
|
|
99
|
+
fs.renameSync(unpacked, extensionDirectory);
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
if (!fs.existsSync(extensionDirectory) && fs.existsSync(previousDirectory))
|
|
103
|
+
fs.renameSync(previousDirectory, extensionDirectory);
|
|
104
|
+
throw error;
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
const checkAndUpdate = async (request) => {
|
|
108
|
+
const config = readConfig();
|
|
109
|
+
if (request.extensionId !== config.extensionId)
|
|
110
|
+
throw new Error('未授权的扩展尝试调用更新器。');
|
|
111
|
+
const response = await fetchJson(`${config.registryUrl}/v1/extension-releases/latest`, config.allowInsecure);
|
|
112
|
+
const checkedAt = Date.now();
|
|
113
|
+
if (!response.release)
|
|
114
|
+
return { status: 'up_to_date', currentVersion: request.currentVersion, checkedAt, message: 'Registry 尚未发布插件版本。' };
|
|
115
|
+
const release = response.release;
|
|
116
|
+
validateRelease(release);
|
|
117
|
+
if (release.version === request.currentVersion)
|
|
118
|
+
return { status: 'up_to_date', currentVersion: request.currentVersion, targetVersion: release.version, checkedAt };
|
|
119
|
+
const staging = fs.mkdtempSync(path.join(baseDirectory, 'staging-'));
|
|
120
|
+
try {
|
|
121
|
+
const archive = await downloadAndVerify(release, config, staging);
|
|
122
|
+
const unpacked = unpackAndValidate(archive, staging, release, config);
|
|
123
|
+
replaceExtension(unpacked);
|
|
124
|
+
return { status: 'updated', currentVersion: request.currentVersion, targetVersion: release.version, checkedAt };
|
|
125
|
+
}
|
|
126
|
+
finally {
|
|
127
|
+
fs.rmSync(staging, { recursive: true, force: true });
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
const update = async () => {
|
|
131
|
+
const config = readConfig();
|
|
132
|
+
const manifestPath = path.join(extensionDirectory, 'manifest.json');
|
|
133
|
+
if (!fs.existsSync(manifestPath))
|
|
134
|
+
throw new Error('未找到已安装的插件目录。请先运行 creative-upload-extension-updater install。');
|
|
135
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
136
|
+
if (typeof manifest.version !== 'string')
|
|
137
|
+
throw new Error('已安装插件的 manifest 版本无效。');
|
|
138
|
+
return checkAndUpdate({ extensionId: config.extensionId, currentVersion: manifest.version });
|
|
139
|
+
};
|
|
140
|
+
const requestChromeReload = (extensionId) => {
|
|
141
|
+
if (process.platform !== 'darwin')
|
|
142
|
+
return { requested: false, message: '当前平台不支持命令行自动重载。' };
|
|
143
|
+
try {
|
|
144
|
+
execFileSync('/usr/bin/open', ['-g', '-a', 'Google Chrome', `chrome-extension://${extensionId}/options.html?nativeReload=1`], { stdio: 'ignore' });
|
|
145
|
+
return { requested: true };
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
return { requested: false, message: '未能唤起 Google Chrome;请在 chrome://extensions 手动重新加载。' };
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
const installNativeHostLauncher = (hostPath) => {
|
|
152
|
+
const nodePath = process.execPath;
|
|
153
|
+
if (!path.isAbsolute(nodePath))
|
|
154
|
+
throw new Error('无法确定 Native Host 的 Node 运行时路径。');
|
|
155
|
+
if (!path.isAbsolute(hostPath))
|
|
156
|
+
throw new Error('Native Host 脚本路径必须为绝对路径。');
|
|
157
|
+
// Chrome does not inherit an interactive shell's PATH, so /usr/bin/env node
|
|
158
|
+
// cannot resolve an nvm-managed Node binary. Keep a stable launcher with the
|
|
159
|
+
// exact Node executable used during installation.
|
|
160
|
+
fs.mkdirSync(baseDirectory, { recursive: true, mode: 0o700 });
|
|
161
|
+
fs.writeFileSync(nativeHostLauncherPath, `#!/bin/sh\nexec ${JSON.stringify(nodePath)} ${JSON.stringify(hostPath)}\n`, { mode: 0o700 });
|
|
162
|
+
fs.chmodSync(nativeHostLauncherPath, 0o700);
|
|
163
|
+
return nativeHostLauncherPath;
|
|
164
|
+
};
|
|
165
|
+
const install = async ({ registryUrl = TRANSITION_HTTP_REGISTRY_ORIGIN, extensionId = DEFAULT_EXTENSION_ID, publicKey = DEFAULT_PUBLIC_KEY, allowInsecure = false, hostPath }) => {
|
|
166
|
+
assertUrl(registryUrl, allowInsecure);
|
|
167
|
+
if (!/^[a-p]{32}$/.test(extensionId))
|
|
168
|
+
throw new Error('Extension ID 无效。');
|
|
169
|
+
crypto.createPublicKey(publicKey);
|
|
170
|
+
const config = { registryUrl: registryUrl.replace(/\/$/, ''), extensionId, signingPublicKey: publicKey, allowInsecure };
|
|
171
|
+
writeConfig(config);
|
|
172
|
+
const update = await checkAndUpdate({ extensionId, currentVersion: '0.0.0' });
|
|
173
|
+
const manifestDirectory = path.dirname(nativeHostManifestPath);
|
|
174
|
+
fs.mkdirSync(manifestDirectory, { recursive: true, mode: 0o700 });
|
|
175
|
+
const launcherPath = installNativeHostLauncher(hostPath);
|
|
176
|
+
fs.writeFileSync(nativeHostManifestPath, `${JSON.stringify({ name: HOST_NAME, description: 'Creative Upload Extension Updater', path: launcherPath, type: 'stdio', allowed_origins: [`chrome-extension://${extensionId}/`] }, null, 2)}\n`, { mode: 0o600 });
|
|
177
|
+
return { ...update, extensionDirectory, extensionId };
|
|
178
|
+
};
|
|
179
|
+
const uninstall = () => {
|
|
180
|
+
const nativeHostRegistered = fs.existsSync(nativeHostManifestPath);
|
|
181
|
+
fs.rmSync(nativeHostManifestPath, { force: true });
|
|
182
|
+
fs.rmSync(baseDirectory, { recursive: true, force: true });
|
|
183
|
+
return {
|
|
184
|
+
nativeHostRemoved: nativeHostRegistered,
|
|
185
|
+
extensionDirectory,
|
|
186
|
+
extensionRemoved: !fs.existsSync(extensionDirectory),
|
|
187
|
+
chromeAction: '请在 chrome://extensions 手动移除已加载的扩展。',
|
|
188
|
+
};
|
|
189
|
+
};
|
|
190
|
+
const rollback = () => {
|
|
191
|
+
if (!fs.existsSync(previousDirectory))
|
|
192
|
+
throw new Error('没有可回滚的上一版本。');
|
|
193
|
+
const currentDirectory = `${extensionDirectory}.rollback-${Date.now()}`;
|
|
194
|
+
if (fs.existsSync(extensionDirectory))
|
|
195
|
+
fs.renameSync(extensionDirectory, currentDirectory);
|
|
196
|
+
fs.renameSync(previousDirectory, extensionDirectory);
|
|
197
|
+
fs.rmSync(currentDirectory, { recursive: true, force: true });
|
|
198
|
+
return { extensionDirectory };
|
|
199
|
+
};
|
|
200
|
+
const diagnose = () => {
|
|
201
|
+
const config = readConfig();
|
|
202
|
+
return { extensionDirectory, extensionLoaded: fs.existsSync(path.join(extensionDirectory, 'manifest.json')), nativeHostRegistered: fs.existsSync(nativeHostManifestPath), registryUrl: config.registryUrl, extensionId: config.extensionId };
|
|
203
|
+
};
|
|
204
|
+
module.exports = { DEFAULT_EXTENSION_ID, HOST_NAME, TRANSITION_HTTP_REGISTRY_ORIGIN, checkAndUpdate, diagnose, install, requestChromeReload, rollback, uninstall, update };
|
|
205
|
+
//# sourceMappingURL=updater.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"updater.js","sourceRoot":"","sources":["../src/updater.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,15 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "creative-upload-extension-updater",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Native Messaging updater for the Creative Upload unpacked Chrome extension.",
|
|
6
6
|
"scripts": {
|
|
7
|
+
"build": "tsc -p tsconfig.json",
|
|
8
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
9
|
+
"prepack": "npm run build",
|
|
7
10
|
"postinstall": "node scripts/postinstall.js"
|
|
8
11
|
},
|
|
9
12
|
"bin": {
|
|
10
|
-
"creative-upload-updater": "
|
|
13
|
+
"creative-upload-updater": "dist/cli.js"
|
|
11
14
|
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"scripts",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
12
20
|
"engines": {
|
|
13
21
|
"node": ">=18"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/node": "^22.20.2",
|
|
25
|
+
"typescript": "^5.9.3"
|
|
14
26
|
}
|
|
15
27
|
}
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
const fs = require('node:fs');
|
|
3
|
-
const path = require('node:path');
|
|
4
|
-
const { diagnose, install, requestChromeReload, rollback, uninstall, update, TRANSITION_HTTP_REGISTRY_ORIGIN } = require('../lib/updater');
|
|
5
|
-
|
|
6
|
-
const packageVersion = require('../package.json').version;
|
|
7
|
-
const printJson = (value) => process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
|
|
8
|
-
const printResult = (args, value, lines) => {
|
|
9
|
-
if (args.json === true) return printJson(value);
|
|
10
|
-
process.stdout.write(`${lines.join('\n')}\n`);
|
|
11
|
-
};
|
|
12
|
-
const printInstallGuide = (extensionDirectory) => {
|
|
13
|
-
process.stdout.write('\n安装完成。请在 Chrome 完成一次加载:\n\n');
|
|
14
|
-
process.stdout.write(' 1. 打开 chrome://extensions\n');
|
|
15
|
-
process.stdout.write(' 2. 开启右上角「开发者模式」\n');
|
|
16
|
-
process.stdout.write(' 3. 点击「加载已解压的扩展程序」\n');
|
|
17
|
-
process.stdout.write(` 4. 在文件选择器中按 Command + Shift + G,粘贴并打开:${extensionDirectory}\n\n`);
|
|
18
|
-
process.stdout.write('后续发布新版本后,扩展会定时检查更新;也可手动运行 creative-upload-updater update。\n');
|
|
19
|
-
};
|
|
20
|
-
const usage = `用法:creative-upload-updater <命令> [选项]
|
|
21
|
-
|
|
22
|
-
命令:
|
|
23
|
-
install [--registry <url>] [--allow-insecure] 安装更新器与首次插件版本
|
|
24
|
-
update [--no-reload] 下载并应用最新插件版本
|
|
25
|
-
diagnose 输出本机安装诊断
|
|
26
|
-
rollback 回滚到上一插件版本
|
|
27
|
-
uninstall 移除更新器注册、插件文件与历史版本
|
|
28
|
-
|
|
29
|
-
选项:
|
|
30
|
-
--help, -h 显示帮助
|
|
31
|
-
--version, -v 显示版本
|
|
32
|
-
--json 以 JSON 输出命令结果`;
|
|
33
|
-
|
|
34
|
-
const parseArguments = (values) =>
|
|
35
|
-
values.reduce((result, value, index) => {
|
|
36
|
-
if (!value.startsWith('--')) return result;
|
|
37
|
-
const key = value.slice(2);
|
|
38
|
-
const next = values[index + 1];
|
|
39
|
-
result[key] = next && !next.startsWith('--') ? next : true;
|
|
40
|
-
return result;
|
|
41
|
-
}, {});
|
|
42
|
-
|
|
43
|
-
const main = async () => {
|
|
44
|
-
const [command] = process.argv.slice(2);
|
|
45
|
-
const args = parseArguments(process.argv.slice(3));
|
|
46
|
-
if (!command || command === '--help' || command === '-h' || args.help === true) {
|
|
47
|
-
process.stdout.write(`${usage}\n`);
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
if (command === '--version' || command === '-v' || args.version === true) {
|
|
51
|
-
process.stdout.write(`${packageVersion}\n`);
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
if (command === 'install') {
|
|
55
|
-
const publicKey =
|
|
56
|
-
typeof args['public-key'] === 'string'
|
|
57
|
-
? fs.readFileSync(path.resolve(args['public-key']), 'utf8')
|
|
58
|
-
: undefined;
|
|
59
|
-
const result = await install({
|
|
60
|
-
registryUrl: typeof args.registry === 'string' ? args.registry : TRANSITION_HTTP_REGISTRY_ORIGIN,
|
|
61
|
-
extensionId: typeof args['extension-id'] === 'string' ? args['extension-id'] : undefined,
|
|
62
|
-
publicKey,
|
|
63
|
-
allowInsecure: args['allow-insecure'] === true,
|
|
64
|
-
hostPath: path.resolve(__dirname, 'native-host.js'),
|
|
65
|
-
});
|
|
66
|
-
printResult(args, result, [
|
|
67
|
-
'插件更新器安装完成。',
|
|
68
|
-
`已安装插件版本:${result.targetVersion ?? result.currentVersion}`,
|
|
69
|
-
`扩展目录:${result.extensionDirectory}`,
|
|
70
|
-
]);
|
|
71
|
-
printInstallGuide(result.extensionDirectory);
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
if (command === 'diagnose') {
|
|
75
|
-
const result = diagnose();
|
|
76
|
-
return printResult(args, result, [
|
|
77
|
-
'更新器诊断结果:',
|
|
78
|
-
`扩展目录:${result.extensionDirectory}`,
|
|
79
|
-
`扩展文件:${result.extensionLoaded ? '已找到' : '未找到'}`,
|
|
80
|
-
`Native Messaging:${result.nativeHostRegistered ? '已注册' : '未注册'}`,
|
|
81
|
-
`更新源:${result.registryUrl}`,
|
|
82
|
-
`扩展 ID:${result.extensionId}`,
|
|
83
|
-
]);
|
|
84
|
-
}
|
|
85
|
-
if (command === 'update') {
|
|
86
|
-
const result = await update();
|
|
87
|
-
if (result.status === 'updated' && args['no-reload'] !== true) {
|
|
88
|
-
const diagnostic = diagnose();
|
|
89
|
-
Object.assign(result, { chromeReload: requestChromeReload(diagnostic.extensionId) });
|
|
90
|
-
}
|
|
91
|
-
const changed = result.status === 'updated';
|
|
92
|
-
return printResult(args, result, changed
|
|
93
|
-
? [`更新完成:${result.currentVersion} → ${result.targetVersion}`, 'Chrome 将自动重新加载扩展;如未生效,请在 chrome://extensions 手动重新加载。']
|
|
94
|
-
: [`已是最新版本:${result.currentVersion}`]);
|
|
95
|
-
}
|
|
96
|
-
if (command === 'rollback') {
|
|
97
|
-
const result = rollback();
|
|
98
|
-
return printResult(args, result, ['已回滚到上一插件版本。', `扩展目录:${result.extensionDirectory}`, '请在 chrome://extensions 手动重新加载扩展。']);
|
|
99
|
-
}
|
|
100
|
-
if (command === 'uninstall') {
|
|
101
|
-
const result = uninstall();
|
|
102
|
-
return printResult(args, result, ['卸载完成。', '已删除更新器注册、插件文件与历史版本。', '请在 chrome://extensions 手动移除已加载的扩展。']);
|
|
103
|
-
}
|
|
104
|
-
throw new Error(`未知命令:${command}\n\n${usage}`);
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
main().catch((error) => {
|
|
108
|
-
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
109
|
-
process.exitCode = 1;
|
|
110
|
-
});
|
package/bin/native-host.js
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
const { checkAndUpdate } = require('../lib/updater');
|
|
3
|
-
|
|
4
|
-
let input = Buffer.alloc(0);
|
|
5
|
-
const respond = (value) => {
|
|
6
|
-
const body = Buffer.from(JSON.stringify(value));
|
|
7
|
-
const header = Buffer.alloc(4);
|
|
8
|
-
header.writeUInt32LE(body.length, 0);
|
|
9
|
-
process.stdout.write(Buffer.concat([header, body]));
|
|
10
|
-
};
|
|
11
|
-
process.stdin.on('data', (chunk) => {
|
|
12
|
-
input = Buffer.concat([input, chunk]);
|
|
13
|
-
if (input.length < 4) return;
|
|
14
|
-
const length = input.readUInt32LE(0);
|
|
15
|
-
if (input.length < length + 4) return;
|
|
16
|
-
const message = JSON.parse(input.subarray(4, length + 4).toString('utf8'));
|
|
17
|
-
if (message.type !== 'check_and_update')
|
|
18
|
-
return respond({ status: 'failed', message: '不支持的更新器请求。' });
|
|
19
|
-
void checkAndUpdate(message)
|
|
20
|
-
.then(respond)
|
|
21
|
-
.catch((error) =>
|
|
22
|
-
respond({
|
|
23
|
-
status: 'failed',
|
|
24
|
-
currentVersion: message.currentVersion,
|
|
25
|
-
checkedAt: Date.now(),
|
|
26
|
-
message: error instanceof Error ? error.message : '插件更新失败。',
|
|
27
|
-
}),
|
|
28
|
-
);
|
|
29
|
-
});
|
package/lib/updater.js
DELETED
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
const crypto = require('node:crypto');
|
|
2
|
-
const fs = require('node:fs');
|
|
3
|
-
const os = require('node:os');
|
|
4
|
-
const path = require('node:path');
|
|
5
|
-
const { execFileSync } = require('node:child_process');
|
|
6
|
-
|
|
7
|
-
const HOST_NAME = 'com.bytedance.creative_upload_extension_updater';
|
|
8
|
-
const DEFAULT_EXTENSION_ID = 'amfhocfbdlbooohhoinhhhgobfdmaljc';
|
|
9
|
-
const TRANSITION_HTTP_REGISTRY_ORIGIN = 'http://10.37.83.141:3100';
|
|
10
|
-
const DEFAULT_PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
|
|
11
|
-
MCowBQYDK2VwAyEAlMYzNfVA3wLMfEniZDN3VQ+hfVXgMspwK6rM4h2P6Mk=
|
|
12
|
-
-----END PUBLIC KEY-----
|
|
13
|
-
`;
|
|
14
|
-
const baseDirectory = path.join(os.homedir(), 'Library', 'Application Support', 'CreativeUploadInspector');
|
|
15
|
-
const configPath = path.join(baseDirectory, 'updater.json');
|
|
16
|
-
const extensionDirectory = path.join(baseDirectory, 'extension');
|
|
17
|
-
const previousDirectory = path.join(baseDirectory, 'previous');
|
|
18
|
-
const nativeHostLauncherPath = path.join(baseDirectory, 'native-host');
|
|
19
|
-
const nativeHostManifestPath = path.join(
|
|
20
|
-
os.homedir(),
|
|
21
|
-
'Library',
|
|
22
|
-
'Application Support',
|
|
23
|
-
'Google',
|
|
24
|
-
'Chrome',
|
|
25
|
-
'NativeMessagingHosts',
|
|
26
|
-
`${HOST_NAME}.json`,
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
const readConfig = () => {
|
|
30
|
-
if (!fs.existsSync(configPath)) throw new Error('更新器尚未安装。请运行 creative-upload-extension-updater install。');
|
|
31
|
-
return JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
32
|
-
};
|
|
33
|
-
const writeConfig = (config) => {
|
|
34
|
-
fs.mkdirSync(baseDirectory, { recursive: true, mode: 0o700 });
|
|
35
|
-
fs.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`, { mode: 0o600 });
|
|
36
|
-
};
|
|
37
|
-
const assertUrl = (url, allowInsecure) => {
|
|
38
|
-
const parsed = new URL(url);
|
|
39
|
-
const transitionRegistry = parsed.origin === TRANSITION_HTTP_REGISTRY_ORIGIN;
|
|
40
|
-
if (parsed.protocol === 'http:' && !allowInsecure && !transitionRegistry) {
|
|
41
|
-
throw new Error(`仅允许 HTTPS 或当前过渡 Registry ${TRANSITION_HTTP_REGISTRY_ORIGIN}。其他 HTTP 地址需显式传入 --allow-insecure。`);
|
|
42
|
-
}
|
|
43
|
-
if (!['https:', 'http:'].includes(parsed.protocol)) {
|
|
44
|
-
throw new Error('更新源 URL 协议无效。');
|
|
45
|
-
}
|
|
46
|
-
return parsed;
|
|
47
|
-
};
|
|
48
|
-
const fetchJson = async (url, allowInsecure) => {
|
|
49
|
-
assertUrl(url, allowInsecure);
|
|
50
|
-
const response = await fetch(url);
|
|
51
|
-
if (!response.ok) throw new Error(`读取更新元数据失败(HTTP ${response.status})。`);
|
|
52
|
-
return response.json();
|
|
53
|
-
};
|
|
54
|
-
const extensionIdFromKey = (key) => {
|
|
55
|
-
const hash = crypto.createHash('sha256').update(Buffer.from(key, 'base64')).digest('hex').slice(0, 32);
|
|
56
|
-
return hash.replace(/[0-9a-f]/g, (digit) => 'abcdefghijklmnop'[Number.parseInt(digit, 16)]);
|
|
57
|
-
};
|
|
58
|
-
const validateRelease = (release) => {
|
|
59
|
-
if (!release || typeof release !== 'object') throw new Error('更新元数据格式无效。');
|
|
60
|
-
for (const field of ['version', 'artifactUrl', 'sha256', 'signature']) {
|
|
61
|
-
if (typeof release[field] !== 'string' || !release[field]) throw new Error(`更新元数据缺少 ${field}。`);
|
|
62
|
-
}
|
|
63
|
-
if (!/^[a-f0-9]{64}$/i.test(release.sha256)) throw new Error('更新元数据中的 SHA256 无效。');
|
|
64
|
-
};
|
|
65
|
-
const downloadAndVerify = async (release, config, staging) => {
|
|
66
|
-
assertUrl(release.artifactUrl, config.allowInsecure);
|
|
67
|
-
const response = await fetch(release.artifactUrl);
|
|
68
|
-
if (!response.ok) throw new Error(`下载插件制品失败(HTTP ${response.status})。`);
|
|
69
|
-
const content = Buffer.from(await response.arrayBuffer());
|
|
70
|
-
const checksum = crypto.createHash('sha256').update(content).digest('hex');
|
|
71
|
-
if (!crypto.timingSafeEqual(Buffer.from(checksum), Buffer.from(release.sha256))) throw new Error('插件制品 SHA256 校验失败。');
|
|
72
|
-
if (!crypto.verify(null, content, config.signingPublicKey, Buffer.from(release.signature, 'base64'))) throw new Error('插件制品签名校验失败。');
|
|
73
|
-
const archive = path.join(staging, 'extension.zip');
|
|
74
|
-
fs.writeFileSync(archive, content, { mode: 0o600 });
|
|
75
|
-
return archive;
|
|
76
|
-
};
|
|
77
|
-
const unpackAndValidate = (archive, staging, release, config) => {
|
|
78
|
-
const unpacked = path.join(staging, 'unpacked');
|
|
79
|
-
fs.mkdirSync(unpacked, { recursive: true, mode: 0o700 });
|
|
80
|
-
execFileSync('/usr/bin/ditto', ['-x', '-k', archive, unpacked], { stdio: 'pipe' });
|
|
81
|
-
const manifestPath = path.join(unpacked, 'manifest.json');
|
|
82
|
-
if (!fs.existsSync(manifestPath)) throw new Error('插件制品缺少 manifest.json。');
|
|
83
|
-
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
84
|
-
if (manifest.version !== release.version) throw new Error('插件制品版本与发布元数据不一致。');
|
|
85
|
-
if (typeof manifest.key !== 'string' || extensionIdFromKey(manifest.key) !== config.extensionId) throw new Error('插件制品的 Extension ID 不匹配。');
|
|
86
|
-
return unpacked;
|
|
87
|
-
};
|
|
88
|
-
const replaceExtension = (unpacked) => {
|
|
89
|
-
const failedDirectory = `${extensionDirectory}.failed-${Date.now()}`;
|
|
90
|
-
fs.rmSync(failedDirectory, { recursive: true, force: true });
|
|
91
|
-
try {
|
|
92
|
-
if (fs.existsSync(previousDirectory)) fs.rmSync(previousDirectory, { recursive: true, force: true });
|
|
93
|
-
if (fs.existsSync(extensionDirectory)) fs.renameSync(extensionDirectory, previousDirectory);
|
|
94
|
-
fs.renameSync(unpacked, extensionDirectory);
|
|
95
|
-
} catch (error) {
|
|
96
|
-
if (!fs.existsSync(extensionDirectory) && fs.existsSync(previousDirectory)) fs.renameSync(previousDirectory, extensionDirectory);
|
|
97
|
-
throw error;
|
|
98
|
-
}
|
|
99
|
-
};
|
|
100
|
-
const checkAndUpdate = async (request) => {
|
|
101
|
-
const config = readConfig();
|
|
102
|
-
if (request.extensionId !== config.extensionId) throw new Error('未授权的扩展尝试调用更新器。');
|
|
103
|
-
const response = await fetchJson(`${config.registryUrl}/v1/extension-releases/latest`, config.allowInsecure);
|
|
104
|
-
const checkedAt = Date.now();
|
|
105
|
-
if (!response.release) return { status: 'up_to_date', currentVersion: request.currentVersion, checkedAt, message: 'Registry 尚未发布插件版本。' };
|
|
106
|
-
const release = response.release;
|
|
107
|
-
validateRelease(release);
|
|
108
|
-
if (release.version === request.currentVersion) return { status: 'up_to_date', currentVersion: request.currentVersion, targetVersion: release.version, checkedAt };
|
|
109
|
-
const staging = fs.mkdtempSync(path.join(baseDirectory, 'staging-'));
|
|
110
|
-
try {
|
|
111
|
-
const archive = await downloadAndVerify(release, config, staging);
|
|
112
|
-
const unpacked = unpackAndValidate(archive, staging, release, config);
|
|
113
|
-
replaceExtension(unpacked);
|
|
114
|
-
return { status: 'updated', currentVersion: request.currentVersion, targetVersion: release.version, checkedAt };
|
|
115
|
-
} finally {
|
|
116
|
-
fs.rmSync(staging, { recursive: true, force: true });
|
|
117
|
-
}
|
|
118
|
-
};
|
|
119
|
-
const update = async () => {
|
|
120
|
-
const config = readConfig();
|
|
121
|
-
const manifestPath = path.join(extensionDirectory, 'manifest.json');
|
|
122
|
-
if (!fs.existsSync(manifestPath)) throw new Error('未找到已安装的插件目录。请先运行 creative-upload-extension-updater install。');
|
|
123
|
-
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
124
|
-
if (typeof manifest.version !== 'string') throw new Error('已安装插件的 manifest 版本无效。');
|
|
125
|
-
return checkAndUpdate({ extensionId: config.extensionId, currentVersion: manifest.version });
|
|
126
|
-
};
|
|
127
|
-
const requestChromeReload = (extensionId) => {
|
|
128
|
-
if (process.platform !== 'darwin') return { requested: false, message: '当前平台不支持命令行自动重载。' };
|
|
129
|
-
try {
|
|
130
|
-
execFileSync('/usr/bin/open', ['-g', '-a', 'Google Chrome', `chrome-extension://${extensionId}/options.html?nativeReload=1`], { stdio: 'ignore' });
|
|
131
|
-
return { requested: true };
|
|
132
|
-
} catch {
|
|
133
|
-
return { requested: false, message: '未能唤起 Google Chrome;请在 chrome://extensions 手动重新加载。' };
|
|
134
|
-
}
|
|
135
|
-
};
|
|
136
|
-
const installNativeHostLauncher = (hostPath) => {
|
|
137
|
-
const nodePath = process.execPath;
|
|
138
|
-
if (!path.isAbsolute(nodePath)) throw new Error('无法确定 Native Host 的 Node 运行时路径。');
|
|
139
|
-
if (!path.isAbsolute(hostPath)) throw new Error('Native Host 脚本路径必须为绝对路径。');
|
|
140
|
-
|
|
141
|
-
// Chrome does not inherit an interactive shell's PATH, so /usr/bin/env node
|
|
142
|
-
// cannot resolve an nvm-managed Node binary. Keep a stable launcher with the
|
|
143
|
-
// exact Node executable used during installation.
|
|
144
|
-
fs.mkdirSync(baseDirectory, { recursive: true, mode: 0o700 });
|
|
145
|
-
fs.writeFileSync(
|
|
146
|
-
nativeHostLauncherPath,
|
|
147
|
-
`#!/bin/sh\nexec ${JSON.stringify(nodePath)} ${JSON.stringify(hostPath)}\n`,
|
|
148
|
-
{ mode: 0o700 },
|
|
149
|
-
);
|
|
150
|
-
fs.chmodSync(nativeHostLauncherPath, 0o700);
|
|
151
|
-
return nativeHostLauncherPath;
|
|
152
|
-
};
|
|
153
|
-
const install = async ({ registryUrl = TRANSITION_HTTP_REGISTRY_ORIGIN, extensionId = DEFAULT_EXTENSION_ID, publicKey = DEFAULT_PUBLIC_KEY, allowInsecure = false, hostPath }) => {
|
|
154
|
-
assertUrl(registryUrl, allowInsecure);
|
|
155
|
-
if (!/^[a-p]{32}$/.test(extensionId)) throw new Error('Extension ID 无效。');
|
|
156
|
-
crypto.createPublicKey(publicKey);
|
|
157
|
-
const config = { registryUrl: registryUrl.replace(/\/$/, ''), extensionId, signingPublicKey: publicKey, allowInsecure };
|
|
158
|
-
writeConfig(config);
|
|
159
|
-
const update = await checkAndUpdate({ extensionId, currentVersion: '0.0.0' });
|
|
160
|
-
const manifestDirectory = path.dirname(nativeHostManifestPath);
|
|
161
|
-
fs.mkdirSync(manifestDirectory, { recursive: true, mode: 0o700 });
|
|
162
|
-
const launcherPath = installNativeHostLauncher(hostPath);
|
|
163
|
-
fs.writeFileSync(nativeHostManifestPath, `${JSON.stringify({ name: HOST_NAME, description: 'Creative Upload Extension Updater', path: launcherPath, type: 'stdio', allowed_origins: [`chrome-extension://${extensionId}/`] }, null, 2)}\n`, { mode: 0o600 });
|
|
164
|
-
return { ...update, extensionDirectory, extensionId };
|
|
165
|
-
};
|
|
166
|
-
const uninstall = () => {
|
|
167
|
-
const nativeHostRegistered = fs.existsSync(nativeHostManifestPath);
|
|
168
|
-
fs.rmSync(nativeHostManifestPath, { force: true });
|
|
169
|
-
fs.rmSync(baseDirectory, { recursive: true, force: true });
|
|
170
|
-
return {
|
|
171
|
-
nativeHostRemoved: nativeHostRegistered,
|
|
172
|
-
extensionDirectory,
|
|
173
|
-
extensionRemoved: !fs.existsSync(extensionDirectory),
|
|
174
|
-
chromeAction: '请在 chrome://extensions 手动移除已加载的扩展。',
|
|
175
|
-
};
|
|
176
|
-
};
|
|
177
|
-
const rollback = () => {
|
|
178
|
-
if (!fs.existsSync(previousDirectory)) throw new Error('没有可回滚的上一版本。');
|
|
179
|
-
const currentDirectory = `${extensionDirectory}.rollback-${Date.now()}`;
|
|
180
|
-
if (fs.existsSync(extensionDirectory)) fs.renameSync(extensionDirectory, currentDirectory);
|
|
181
|
-
fs.renameSync(previousDirectory, extensionDirectory);
|
|
182
|
-
fs.rmSync(currentDirectory, { recursive: true, force: true });
|
|
183
|
-
return { extensionDirectory };
|
|
184
|
-
};
|
|
185
|
-
const diagnose = () => {
|
|
186
|
-
const config = readConfig();
|
|
187
|
-
return { extensionDirectory, extensionLoaded: fs.existsSync(path.join(extensionDirectory, 'manifest.json')), nativeHostRegistered: fs.existsSync(nativeHostManifestPath), registryUrl: config.registryUrl, extensionId: config.extensionId };
|
|
188
|
-
};
|
|
189
|
-
|
|
190
|
-
module.exports = { DEFAULT_EXTENSION_ID, HOST_NAME, TRANSITION_HTTP_REGISTRY_ORIGIN, checkAndUpdate, diagnose, install, requestChromeReload, rollback, uninstall, update };
|