gm-qwen 2.0.1004 → 2.0.1005

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/bin/bootstrap.js CHANGED
@@ -117,29 +117,70 @@ function gmToolsDir() {
117
117
  // through node. Self-update inside the Rust binary keeps gm-tools fresh from
118
118
  // here on. Skipped silently on any error — the next session-start hook will
119
119
  // retry via ensure_tools_current.
120
- function copyToGmTools(finalPath, wrapperDir, version) {
120
+ function killHoldersOfPath(targetPath) {
121
+ if (process.platform !== 'win32') return 0;
121
122
  try {
122
- const dst = gmToolsDir();
123
- fs.mkdirSync(dst, { recursive: true });
124
- const exeName = process.platform === 'win32' ? 'plugkit.exe' : 'plugkit';
125
- const target = path.join(dst, exeName);
126
- const targetTmp = target + '.new';
127
- fs.copyFileSync(finalPath, targetTmp);
128
- try { fs.renameSync(targetTmp, target); }
129
- catch (err) {
130
- if (err.code === 'EEXIST' || err.code === 'EPERM' || err.code === 'EBUSY') {
131
- // target may be locked by a running plugkit; the .new file persists
132
- // and the in-Rust self-update will eventually swap it. Leave it.
133
- } else { throw err; }
123
+ const { spawnSync } = require('child_process');
124
+ const norm = path.resolve(targetPath).replace(/\//g, '\\');
125
+ const r = spawnSync('wmic', ['process', 'where', `ExecutablePath='${norm.replace(/\\/g, '\\\\')}'`, 'get', 'ProcessId', '/format:value'], { encoding: 'utf8', windowsHide: true, timeout: 5000 });
126
+ if (r.status !== 0 || !r.stdout) return 0;
127
+ const pids = [];
128
+ for (const line of r.stdout.split(/\r?\n/)) {
129
+ const m = line.match(/ProcessId=(\d+)/);
130
+ if (m) {
131
+ const pid = parseInt(m[1], 10);
132
+ if (Number.isFinite(pid) && pid !== process.pid) pids.push(pid);
133
+ }
134
134
  }
135
- if (process.platform !== 'win32') {
136
- try { fs.chmodSync(target, 0o755); } catch (_) {}
135
+ for (const pid of pids) {
136
+ try { spawnSync('taskkill', ['/F', '/PID', String(pid)], { windowsHide: true, timeout: 3000 }); } catch (_) {}
137
+ }
138
+ return pids.length;
139
+ } catch (_) { return 0; }
140
+ }
141
+
142
+ function cleanOrphanNewFiles(dst, exeName) {
143
+ try {
144
+ for (const name of fs.readdirSync(dst)) {
145
+ if (name === exeName + '.new') continue;
146
+ if (/^plugkit(\.\d+\.\d+\.\d+)?\.new$/i.test(name)) {
147
+ try { fs.unlinkSync(path.join(dst, name)); } catch (_) {}
148
+ }
137
149
  }
138
- fs.writeFileSync(path.join(dst, 'plugkit.version'), version);
139
- try {
140
- const srcSha = path.join(wrapperDir, 'plugkit.sha256');
141
- if (fs.existsSync(srcSha)) fs.copyFileSync(srcSha, path.join(dst, 'plugkit.sha256'));
142
- } catch (_) {}
150
+ } catch (_) {}
151
+ }
152
+
153
+ function renameWithRetry(src, dst, attempts) {
154
+ for (let i = 0; i < attempts; i++) {
155
+ try { fs.renameSync(src, dst); return true; }
156
+ catch (err) {
157
+ if (err.code !== 'EEXIST' && err.code !== 'EPERM' && err.code !== 'EBUSY' && err.code !== 'EACCES') throw err;
158
+ if (i === Math.floor(attempts / 2)) killHoldersOfPath(dst);
159
+ try { const { spawnSync } = require('child_process'); spawnSync(process.execPath, ['-e', 'setTimeout(()=>{}, 200)'], { timeout: 400, killSignal: 'SIGKILL', stdio: 'ignore', windowsHide: true }); } catch (_) {}
160
+ }
161
+ }
162
+ return false;
163
+ }
164
+
165
+ function copyToGmTools(finalPath, wrapperDir, version) {
166
+ const dst = gmToolsDir();
167
+ fs.mkdirSync(dst, { recursive: true });
168
+ const exeName = process.platform === 'win32' ? 'plugkit.exe' : 'plugkit';
169
+ const target = path.join(dst, exeName);
170
+ const targetTmp = target + '.new';
171
+ cleanOrphanNewFiles(dst, exeName);
172
+ fs.copyFileSync(finalPath, targetTmp);
173
+ if (!renameWithRetry(targetTmp, target, 8)) {
174
+ obsEvent('bootstrap', 'gmtools.rename.failed', { target });
175
+ throw new Error(`gm-tools update blocked: cannot replace ${target} (held open by running plugkit and kill-retry exhausted)`);
176
+ }
177
+ if (process.platform !== 'win32') {
178
+ try { fs.chmodSync(target, 0o755); } catch (_) {}
179
+ }
180
+ fs.writeFileSync(path.join(dst, 'plugkit.version'), version);
181
+ try {
182
+ const srcSha = path.join(wrapperDir, 'plugkit.sha256');
183
+ if (fs.existsSync(srcSha)) fs.copyFileSync(srcSha, path.join(dst, 'plugkit.sha256'));
143
184
  } catch (_) {}
144
185
  }
145
186
 
@@ -804,6 +845,19 @@ if (require.main === module) {
804
845
  } else {
805
846
  bootstrap({ silent: false })
806
847
  .then(p => { process.stdout.write(p + '\n'); process.exit(0); })
807
- .catch(err => { log(`FATAL: ${err.message}`); obsEvent('bootstrap', 'fatal', { err: String(err.message || err) }); process.exit(1); });
848
+ .catch(err => {
849
+ log(`FATAL: ${err.message}`);
850
+ obsEvent('bootstrap', 'fatal', { err: String(err.message || err) });
851
+ try {
852
+ const pinned = (() => { try { return readVersionFile(__dirname); } catch (_) { return null; } })();
853
+ writeBootstrapError({
854
+ expected_version: pinned,
855
+ cached_version: null,
856
+ error_phase: 'fatal',
857
+ error_message: String(err && err.message || err),
858
+ });
859
+ } catch (_) {}
860
+ process.exit(1);
861
+ });
808
862
  }
809
863
  }
package/gm.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm",
3
- "version": "2.0.1004",
3
+ "version": "2.0.1005",
4
4
  "description": "State machine agent with hooks, skills, and automated git enforcement",
5
5
  "author": "AnEntrypoint",
6
6
  "license": "MIT",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-qwen",
3
- "version": "2.0.1004",
3
+ "version": "2.0.1005",
4
4
  "description": "State machine agent with hooks, skills, and automated git enforcement",
5
5
  "author": "AnEntrypoint",
6
6
  "license": "MIT",