@myassis/gateway 1.0.21 → 1.0.23

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.
@@ -34,6 +34,9 @@ const fs = __importStar(require("fs"));
34
34
  * dist/config/index.js → 上三级 = 包根目录
35
35
  */
36
36
  function getPackageRootDir() {
37
+ if (process.pkg) {
38
+ return path.dirname(process.execPath);
39
+ }
37
40
  return path.resolve(__dirname, '..', '..');
38
41
  }
39
42
  const getEnvPath = () => {
@@ -42,7 +45,16 @@ const getEnvPath = () => {
42
45
  if (fs.existsSync(cwdEnvPath)) {
43
46
  return cwdEnvPath;
44
47
  }
45
- // 2. 包安装目录(随包发布的默认 .env
48
+ // 2. pkg 环境:从 snapshot 虚拟路径读取打包的 .env
49
+ if (process.pkg) {
50
+ // __dirname 在 pkg 中指向 /snapshot/pocketclaw/gateway/dist/config
51
+ // 打包的 .env 在 /snapshot/pocketclaw/gateway/dist/.env
52
+ const snapshotEnvPath = path.resolve(__dirname, '..', '.env');
53
+ if (fs.existsSync(snapshotEnvPath)) {
54
+ return snapshotEnvPath;
55
+ }
56
+ }
57
+ // 3. 包安装目录(随包发布的默认 .env)
46
58
  const pkgEnvPath = path.join(getPackageRootDir(), 'dist', '.env');
47
59
  if (fs.existsSync(pkgEnvPath)) {
48
60
  return pkgEnvPath;
package/dist/main.js CHANGED
@@ -159,6 +159,7 @@ else {
159
159
  }
160
160
  // 内网地址正则
161
161
  const lanPatterns = [
162
+ /^http:\/\/tauri.localhost(:\d+)?$/,
162
163
  /^http:\/\/localhost(:\d+)?$/,
163
164
  /^http:\/\/127\.\d+\.\d+\.\d+(:\d+)?$/,
164
165
  /^http:\/\/10\.\d+\.\d+\.\d+(:\d+)?$/,
@@ -433,8 +433,8 @@ router.post('/sessions/:sessionId/stream', ensureAgentManager, async (req, res)
433
433
  const userId = req.userId;
434
434
  const { sessionId } = req.params;
435
435
  const { content, attachments, userMessageId, assistantMessageId } = req.body;
436
- if (!content) {
437
- return res.status(400).json({ success: false, error: 'Content is required' });
436
+ if (!content && attachments.length === 0) {
437
+ return res.status(400).json({ success: false, error: 'Content or attachments are required' });
438
438
  }
439
439
  // Find session directly
440
440
  const session = (0, index_js_2.getSessionManager)(userId).getSession(sessionId);
@@ -9,11 +9,19 @@ const util_1 = require("util");
9
9
  const path_1 = __importDefault(require("path"));
10
10
  const fs_1 = __importDefault(require("fs"));
11
11
  const axios_1 = __importDefault(require("axios"));
12
+ const shared_1 = require("@myassis/shared");
13
+ const os_1 = __importDefault(require("os"));
14
+ const logger = (0, shared_1.getLogger)('ServiceManager');
12
15
  const execAsync = (0, util_1.promisify)(child_process_1.exec);
13
16
  exports.SERVICE_NAME = 'myassis-gateway';
14
17
  exports.SERVICE_DISPLAY_NAME = '我的助手 Desktop Gateway Service';
15
18
  // ─── 平台无关工具 ────────────────────────────────────────────
19
+ function isPackagedExe() {
20
+ return !!process.pkg;
21
+ }
16
22
  function getServiceScript() {
23
+ if (isPackagedExe())
24
+ return '';
17
25
  // npm install -g 后,用当前模块位置定位包目录
18
26
  const pkgRoot = path_1.default.resolve(__dirname, '..', '..');
19
27
  return path_1.default.join(pkgRoot, 'dist', 'index.js');
@@ -21,10 +29,34 @@ function getServiceScript() {
21
29
  function getNodeExec() {
22
30
  return process.execPath;
23
31
  }
32
+ function getNssmPath() {
33
+ // 1. 优先:exe 同目录下有 nssm.exe
34
+ const alongside = path_1.default.join(path_1.default.dirname(process.execPath), 'nssm.exe');
35
+ if (fs_1.default.existsSync(alongside))
36
+ return alongside;
37
+ // 2. pkg 运行时从包内虚拟文件系统提取 nssm.exe 到临时目录
38
+ if (isPackagedExe()) {
39
+ const tmpPath = path_1.default.join(os_1.default.tmpdir(), 'myassis-nssm.exe');
40
+ if (!fs_1.default.existsSync(tmpPath)) {
41
+ try {
42
+ const bundledPath = path_1.default.join(__dirname, '..', '..', 'nssm.exe');
43
+ const data = fs_1.default.readFileSync(bundledPath);
44
+ fs_1.default.writeFileSync(tmpPath, data);
45
+ logger.info(`Extracted nssm.exe to ${tmpPath}`);
46
+ }
47
+ catch (e) {
48
+ logger.warn(`Failed to extract nssm.exe: ${e.message}`);
49
+ return '';
50
+ }
51
+ }
52
+ return tmpPath;
53
+ }
54
+ return '';
55
+ }
24
56
  // ─── Windows 实现 ───────────────────────────────────────────
25
57
  async function queryServiceWindows() {
26
58
  try {
27
- const { stdout } = await execAsync(`sc query ${exports.SERVICE_NAME}`, { timeout: 5000 });
59
+ const { stdout } = await execAsync(`sc.exe query ${exports.SERVICE_NAME}`, { timeout: 5000 });
28
60
  return { installed: true, running: stdout.includes('RUNNING') };
29
61
  }
30
62
  catch {
@@ -32,23 +64,35 @@ async function queryServiceWindows() {
32
64
  }
33
65
  }
34
66
  async function installWindows() {
67
+ const isPkg = isPackagedExe();
35
68
  const script = getServiceScript();
36
- const workDir = path_1.default.dirname(script);
37
- const nodeExe = getNodeExec();
69
+ const exe = getNodeExec();
70
+ const workDir = isPkg ? path_1.default.dirname(exe) : path_1.default.dirname(script);
71
+ logger.info(`isPkg=${isPkg}, exe=${exe}, script=${script}, workDir=${workDir}`);
38
72
  const { installed } = await queryServiceWindows();
39
73
  if (installed)
40
- await stopService();
41
- const nssmPath = path_1.default.join(path_1.default.dirname(process.execPath), 'nssm.exe');
42
- const useNssm = fs_1.default.existsSync(nssmPath);
74
+ await uninstallWindows();
75
+ const nssmPath = getNssmPath();
76
+ const useNssm = !!nssmPath && fs_1.default.existsSync(nssmPath);
43
77
  try {
44
78
  if (useNssm) {
45
- await execAsync(`"${nssmPath}" install ${exports.SERVICE_NAME} "${nodeExe}" "${script}"`, { timeout: 15000, cwd: workDir });
79
+ if (isPkg) {
80
+ await execAsync(`"${nssmPath}" install ${exports.SERVICE_NAME} "${exe}"`, { timeout: 15000, cwd: workDir });
81
+ }
82
+ else {
83
+ await execAsync(`"${nssmPath}" install ${exports.SERVICE_NAME} "${exe}" "${script}"`, { timeout: 15000, cwd: workDir });
84
+ }
46
85
  await execAsync(`"${nssmPath}" set ${exports.SERVICE_NAME} AppDirectory "${workDir}"`, { timeout: 10000 });
47
86
  await execAsync(`"${nssmPath}" set ${exports.SERVICE_NAME} DisplayName "${exports.SERVICE_DISPLAY_NAME}"`, { timeout: 10000 });
48
87
  await execAsync(`"${nssmPath}" set ${exports.SERVICE_NAME} Start SERVICE_AUTO_START`, { timeout: 10000 });
49
88
  }
50
89
  else {
51
- await execAsync(`sc create ${exports.SERVICE_NAME} binPath= "\\"${nodeExe}\\" \\"${script}\\"" DisplayName= "${exports.SERVICE_DISPLAY_NAME}" start= auto`, { timeout: 15000 });
90
+ if (isPkg) {
91
+ await execAsync(`sc.exe create ${exports.SERVICE_NAME} binPath= "\\"${exe}\\"" DisplayName= "${exports.SERVICE_DISPLAY_NAME}" start= auto`, { timeout: 15000 });
92
+ }
93
+ else {
94
+ await execAsync(`sc.exe create ${exports.SERVICE_NAME} binPath= "\\"${exe}\\" \\"${script}\\"" DisplayName= "${exports.SERVICE_DISPLAY_NAME}" start= auto`, { timeout: 15000 });
95
+ }
52
96
  }
53
97
  await startService();
54
98
  return { success: true, message: '服务安装并启动成功' };
@@ -60,12 +104,12 @@ async function installWindows() {
60
104
  async function uninstallWindows() {
61
105
  try {
62
106
  await stopService();
63
- const nssmPath = path_1.default.join(path_1.default.dirname(process.execPath), 'nssm.exe');
64
- if (fs_1.default.existsSync(nssmPath)) {
107
+ const nssmPath = getNssmPath();
108
+ if (nssmPath && fs_1.default.existsSync(nssmPath)) {
65
109
  await execAsync(`"${nssmPath}" remove ${exports.SERVICE_NAME} confirm`, { timeout: 10000 });
66
110
  }
67
111
  else {
68
- await execAsync(`sc delete ${exports.SERVICE_NAME}`, { timeout: 10000 });
112
+ await execAsync(`sc.exe delete ${exports.SERVICE_NAME}`, { timeout: 10000 });
69
113
  }
70
114
  return { success: true, message: '服务卸载成功' };
71
115
  }
@@ -75,7 +119,7 @@ async function uninstallWindows() {
75
119
  }
76
120
  async function startServiceWindows() {
77
121
  try {
78
- await execAsync(`sc start ${exports.SERVICE_NAME}`, { timeout: 10000 });
122
+ await execAsync(`sc.exe start ${exports.SERVICE_NAME}`, { timeout: 10000 });
79
123
  return { success: true, message: '服务启动成功' };
80
124
  }
81
125
  catch (err) {
@@ -83,7 +127,7 @@ async function startServiceWindows() {
83
127
  }
84
128
  }
85
129
  async function restartServiceWindows() {
86
- const nssmPath = path_1.default.join(path_1.default.dirname(process.execPath), 'nssm.exe');
130
+ const nssmPath = getNssmPath();
87
131
  // 无 nssm 时需要用户手动更新
88
132
  if (!fs_1.default.existsSync(nssmPath)) {
89
133
  const info = await getServiceInfo();
@@ -109,7 +153,7 @@ async function restartServiceWindows() {
109
153
  }
110
154
  async function stopServiceWindows() {
111
155
  try {
112
- await execAsync(`sc stop ${exports.SERVICE_NAME}`, { timeout: 10000 });
156
+ await execAsync(`sc.exe stop ${exports.SERVICE_NAME}`, { timeout: 10000 });
113
157
  return { success: true, message: '服务停止成功' };
114
158
  }
115
159
  catch (err) {
@@ -136,28 +180,30 @@ async function queryServiceLinux() {
136
180
  }
137
181
  }
138
182
  async function installLinux() {
183
+ const isPkg = isPackagedExe();
139
184
  const script = getServiceScript();
140
- const workDir = path_1.default.dirname(script);
141
- const nodeExe = getNodeExec();
185
+ const exe = getNodeExec();
186
+ const workDir = isPkg ? path_1.default.dirname(exe) : path_1.default.dirname(script);
142
187
  try {
143
188
  const { installed } = await queryServiceLinux();
144
189
  if (installed)
145
190
  await stopService();
146
- const unitContent = `[Unit]
147
- Description=${exports.SERVICE_DISPLAY_NAME}
148
- After=network.target
149
-
150
- [Service]
151
- Type=simple
152
- User=${process.env.USER || 'root'}
153
- WorkingDirectory=${workDir}
154
- ExecStart=${nodeExe} ${script}
155
- Restart=always
156
- RestartSec=5
157
- Environment=NODE_ENV=production
158
-
159
- [Install]
160
- WantedBy=multi-user.target
191
+ const execStart = isPkg ? exe : `${exe} ${script}`;
192
+ const unitContent = `[Unit]
193
+ Description=${exports.SERVICE_DISPLAY_NAME}
194
+ After=network.target
195
+
196
+ [Service]
197
+ Type=simple
198
+ User=${process.env.USER || 'root'}
199
+ WorkingDirectory=${workDir}
200
+ ExecStart=${execStart}
201
+ Restart=always
202
+ RestartSec=5
203
+ Environment=NODE_ENV=production
204
+
205
+ [Install]
206
+ WantedBy=multi-user.target
161
207
  `;
162
208
  await fs_1.default.promises.writeFile('/tmp/myassis-gateway.service', unitContent, 'utf8');
163
209
  await execAsync('cp /tmp/myassis-gateway.service /etc/systemd/system/myassis-gateway.service', { timeout: 10000 });
@@ -400,7 +400,7 @@ class LLMClient {
400
400
  }
401
401
  const tokens = JSON.stringify(body).length;
402
402
  logger.debug('tokens', tokens);
403
- if (tokens > 200000) {
403
+ if (tokens > 200000 && !existAttachments) {
404
404
  logger.warn('message is too large and has writen to the file debug.json');
405
405
  await promises_1.default.writeFile('debug.json', JSON.stringify(body.messages), 'utf-8');
406
406
  throw Error('exceed max message tokens');
@@ -110,8 +110,22 @@ class Session {
110
110
  // 将附件统一为 Attachment 对象格式
111
111
  const normalizedAttachments = (attachments || []).map((att) => {
112
112
  if (typeof att === 'string') {
113
- // URL 字符串格式,转换为对象
113
+ // 可能是 base64 data URL 或普通 URL
114
114
  const url = att;
115
+ const mimeMatch = url.match(/^data:([^;]+)/);
116
+ if (mimeMatch) {
117
+ // base64 data URL: 从 MIME 识别类型
118
+ const mime = mimeMatch[1];
119
+ let type = 'file';
120
+ if (mime.startsWith('image/'))
121
+ type = 'image';
122
+ else if (mime.startsWith('video/'))
123
+ type = 'video';
124
+ else if (mime.startsWith('audio/'))
125
+ type = 'audio';
126
+ return { type, name: 'attachment', url };
127
+ }
128
+ // 普通 URL: 从后缀识别类型
115
129
  const name = url.split('/').pop().split('?')[0] || 'attachment';
116
130
  const ext = name.split('.').pop()?.toLowerCase() || '';
117
131
  const imageExts = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp', 'svg'];
@@ -127,10 +141,22 @@ class Session {
127
141
  return { type, name, url };
128
142
  }
129
143
  // 已经是对象格式,确保有必要字段
144
+ const url = att.url || att.uri || '';
145
+ const mimeMatch = url.match(/^data:([^;]+)/);
146
+ let type = att.type || 'file';
147
+ if (!att.type && mimeMatch) {
148
+ const mime = mimeMatch[1];
149
+ if (mime.startsWith('image/'))
150
+ type = 'image';
151
+ else if (mime.startsWith('video/'))
152
+ type = 'video';
153
+ else if (mime.startsWith('audio/'))
154
+ type = 'audio';
155
+ }
130
156
  return {
131
- type: att.type || 'file',
157
+ type,
132
158
  name: att.name || 'attachment',
133
- url: att.url || att.uri || '',
159
+ url,
134
160
  size: att.size,
135
161
  mimeType: att.mimeType,
136
162
  };
@@ -15,6 +15,12 @@ const logger = (0, shared_1.getLogger)('SessionStore');
15
15
  * 用 __dirname 定位,比 process.execPath 可靠
16
16
  */
17
17
  function getPackageRootDir() {
18
+ // pkg 打包后 __dirname 是 /snapshot/... 虚拟路径,无法写文件
19
+ // 用 process.pkg 判断是否在 pkg 环境中
20
+ if (process.pkg) {
21
+ // process.execPath 指向二进制文件本身,取其所在目录
22
+ return path_1.default.dirname(process.execPath);
23
+ }
18
24
  // dist/services/session/SessionStore.js → 包根目录
19
25
  return path_1.default.resolve(__dirname, '..', '..', '..');
20
26
  }
@@ -33,6 +39,13 @@ function getDbPath() {
33
39
  }
34
40
  exports.getDbPath = getDbPath;
35
41
  function getMigrationsDir() {
42
+ // pkg 环境:从 snapshot 虚拟路径读取打包的 migrations
43
+ if (process.pkg) {
44
+ const snapshotMigrations = path_1.default.resolve(__dirname, '..', '..', '..', 'migrations');
45
+ if (fs_1.default.existsSync(snapshotMigrations)) {
46
+ return snapshotMigrations;
47
+ }
48
+ }
36
49
  // migrations 随包发布,从包安装目录读取
37
50
  const pkgMigrations = path_1.default.join(getPackageRootDir(), 'migrations');
38
51
  if (fs_1.default.existsSync(pkgMigrations)) {
@@ -22,13 +22,10 @@ var __importStar = (this && this.__importStar) || function (mod) {
22
22
  __setModuleDefault(result, mod);
23
23
  return result;
24
24
  };
25
- var __importDefault = (this && this.__importDefault) || function (mod) {
26
- return (mod && mod.__esModule) ? mod : { "default": mod };
27
- };
28
25
  Object.defineProperty(exports, "__esModule", { value: true });
29
26
  exports.searchTool = void 0;
30
27
  // @ts-ignore
31
- const got_1 = __importDefault(require("got"));
28
+ const undici_1 = require("undici");
32
29
  const cheerio = __importStar(require("cheerio"));
33
30
  exports.searchTool = {
34
31
  name: 'search',
@@ -44,14 +41,15 @@ exports.searchTool = {
44
41
  handler: async (args) => {
45
42
  try {
46
43
  const { query, limit = 5 } = args;
47
- const response = await (0, got_1.default)(`https://cn.bing.com/search?q=${encodeURIComponent(query)}`, {
44
+ const response = await (0, undici_1.fetch)(`https://cn.bing.com/search?q=${encodeURIComponent(query)}`, {
48
45
  headers: {
49
46
  'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
50
47
  'Accept-Language': 'zh-CN,zh;q=0.9',
51
48
  },
52
- timeout: { request: 10000 },
49
+ signal: AbortSignal.timeout(10000),
53
50
  });
54
- const $ = cheerio.load(response.body);
51
+ const body = await response.text();
52
+ const $ = cheerio.load(body);
55
53
  const results = [];
56
54
  $('li.b_algo').each((_, el) => {
57
55
  if (results.length >= limit)
@@ -22,13 +22,10 @@ var __importStar = (this && this.__importStar) || function (mod) {
22
22
  __setModuleDefault(result, mod);
23
23
  return result;
24
24
  };
25
- var __importDefault = (this && this.__importDefault) || function (mod) {
26
- return (mod && mod.__esModule) ? mod : { "default": mod };
27
- };
28
25
  Object.defineProperty(exports, "__esModule", { value: true });
29
26
  exports.webFetchTool = void 0;
30
27
  // @ts-ignore
31
- const got_1 = __importDefault(require("got"));
28
+ const undici_1 = require("undici");
32
29
  const cheerio = __importStar(require("cheerio"));
33
30
  exports.webFetchTool = {
34
31
  name: 'webFetch',
@@ -44,13 +41,14 @@ exports.webFetchTool = {
44
41
  handler: async (args) => {
45
42
  try {
46
43
  const { url, query } = args;
47
- const response = await (0, got_1.default)(url, {
44
+ const response = await (0, undici_1.fetch)(url, {
48
45
  headers: {
49
46
  'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
50
47
  },
51
- timeout: { request: 15000 },
48
+ signal: AbortSignal.timeout(15000),
52
49
  });
53
- const $ = cheerio.load(response.body);
50
+ const body = await response.text();
51
+ const $ = cheerio.load(body);
54
52
  let content = $.html();
55
53
  if (query) {
56
54
  content = $(query).text().trim() || `选择器 ${query} 未找到匹配内容`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myassis/gateway",
3
- "version": "1.0.21",
3
+ "version": "1.0.23",
4
4
  "description": "我的助手 Gateway Service - 本地 AI 网关服务,支持认证、WebSocket 实时通信和任务调度",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -24,9 +24,10 @@
24
24
  "start": "node dist/index.js",
25
25
  "build": "tsc --skipLibCheck",
26
26
  "postbuild": "node scripts/add-shebang.js && npx tsc-alias",
27
- "pkg:win": "npx @yao-pkg/pkg . --targets node18-win-x64 --output myassis-gateway-win.exe",
28
- "pkg:linux": "npx @yao-pkg/pkg . --targets node18-linux-x64 --output myassis-gateway-linux",
27
+ "pkg:win": "node scripts/pkg-build-win.js node24-win-x64 myassis-gateway-win.exe",
28
+ "pkg:linux": "npx @yao-pkg/pkg . --targets node24-linux-x64 --output myassis-gateway-linux",
29
29
  "pkg:all": "npm run pkg:win && npm run pkg:linux",
30
+ "pkg:linux-analyze": "npx @yao-pkg/pkg . --targets node24-linux-x64 --output myassis-gateway-linux --public-packages got --debug",
30
31
  "test": "jest"
31
32
  },
32
33
  "keywords": [
@@ -39,7 +40,7 @@
39
40
  ],
40
41
  "author": "duzhengjie",
41
42
  "dependencies": {
42
- "@myassis/shared": "1.0.15",
43
+ "@myassis/shared": "workspace:*",
43
44
  "@nut-tree/nut-js": "^4.2.0",
44
45
  "axios": "^1.15.2",
45
46
  "bcryptjs": "^2.4.3",
@@ -49,7 +50,6 @@
49
50
  "cors": "^2.8.5",
50
51
  "dotenv": "^16.3.1",
51
52
  "express": "^4.18.2",
52
- "got": "^15.0.3",
53
53
  "helmet": "^7.1.0",
54
54
  "https-proxy-agent": "^9.0.0",
55
55
  "jsonwebtoken": "^9.0.2",
@@ -61,7 +61,6 @@
61
61
  },
62
62
  "devDependencies": {
63
63
  "@types/bcryptjs": "^2.4.6",
64
- "@types/compression": "^1.7.5",
65
64
  "@types/cors": "^2.8.17",
66
65
  "@types/express": "^4.17.21",
67
66
  "@types/jsonwebtoken": "^9.0.6",
@@ -72,7 +71,8 @@
72
71
  "@yao-pkg/pkg": "^6.20.0",
73
72
  "tsc-alias": "^1.8.10",
74
73
  "tsx": "^4.7.0",
75
- "typescript": "^5.3.3"
74
+ "typescript": "^5.3.3",
75
+ "@yao-pkg/pkg-fetch": "3.6.3"
76
76
  },
77
77
  "pkg": {
78
78
  "scripts": [
@@ -81,11 +81,15 @@
81
81
  "assets": [
82
82
  "dist/**/*",
83
83
  "migrations/**/*",
84
- "README*"
84
+ "README*",
85
+ "node_modules/better-sqlite3/build/Release/better_sqlite3.node",
86
+ "node_modules/.pnpm/@nut-tree+libnut-linux@2.7.1/node_modules/@nut-tree/libnut-linux/build/Release/libnut.node",
87
+ "node_modules/.pnpm/@nut-tree+libnut-win32@2.7.1/node_modules/@nut-tree/libnut-win32/build/Release/libnut.node",
88
+ "nssm.exe"
85
89
  ],
86
90
  "targets": [
87
- "node18-win-x64",
88
- "node18-linux-x64"
91
+ "node24-win-x64",
92
+ "node24-linux-x64"
89
93
  ]
90
94
  }
91
95
  }
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env node
2
+ // pkg-build-win.js - Windows pkg build using PKG_CACHE_PATH
3
+ // Points to ~/.pkg-cache which contains the correct v3.6 patched binary (40.6 MB, hash matches)
4
+ 'use strict';
5
+ const { spawn } = require('child_process');
6
+ const path = require('path');
7
+ const os = require('os');
8
+ const fs = require('fs');
9
+
10
+ const TARGET = process.argv[2] || 'node18-win-x64';
11
+ const OUTPUT = process.argv[3] || 'myassis-gateway-win.exe';
12
+
13
+ const CACHE_DIR = path.join(os.homedir(), '.pkg-cache');
14
+ const V36_BINARY = path.join(CACHE_DIR, 'v3.6', 'fetched-v18.20.8-win-x64');
15
+ const PKG_BIN = path.resolve(__dirname, '../node_modules/@yao-pkg/pkg/lib-es5/bin.js');
16
+
17
+ function log(msg) { console.error('[pkg-build] ' + msg); }
18
+
19
+ async function main() {
20
+ log(`Building for ${TARGET}`);
21
+ log(`Output: ${OUTPUT}`);
22
+
23
+ // Verify v3.6 binary exists
24
+ if (!fs.existsSync(V36_BINARY)) {
25
+ log(`ERROR: Patched binary not found at ${V36_BINARY}`);
26
+ log('Please run: node node_modules/@yao-pkg/pkg/lib-es5/bin.js --targets node18-win-x64 first');
27
+ process.exit(1);
28
+ }
29
+ const size = (fs.statSync(V36_BINARY).size / 1024 / 1024).toFixed(1);
30
+ log(`Using patched binary: ${V36_BINARY} (${size} MB)`);
31
+
32
+ // Use PKG_CACHE_PATH so pkg-fetch finds the correct v3.6 binary by hash
33
+ const env = { ...process.env };
34
+ delete env.PKG_NODE_PATH; // MUST NOT use PKG_NODE_PATH - it bypasses placeholders check
35
+ env.PKG_CACHE_PATH = CACHE_DIR;
36
+ log(`PKG_CACHE_PATH=${CACHE_DIR}`);
37
+
38
+ log('Starting pkg...');
39
+ return new Promise((resolve) => {
40
+ const child = spawn(process.execPath, [PKG_BIN, '.', '--targets', TARGET, '--output', OUTPUT], {
41
+ cwd: path.resolve(__dirname, '..'),
42
+ env,
43
+ stdio: 'inherit'
44
+ });
45
+ child.on('exit', (code) => process.exit(code || 0));
46
+ child.on('error', (e) => { log('Spawn error: ' + e.message); process.exit(1); });
47
+ });
48
+ }
49
+
50
+ main().catch(e => { console.error('[pkg-build] Fatal:', e.message); process.exit(1); });