desktop-pet-app 0.3.1 → 0.3.3

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.
Files changed (2) hide show
  1. package/out/main/index.js +66 -1
  2. package/package.json +1 -1
package/out/main/index.js CHANGED
@@ -3144,6 +3144,67 @@ async function dutyLoop(adapter) {
3144
3144
  }
3145
3145
  }
3146
3146
  }
3147
+ const TAKEOVER_TIMEOUT_MS = 5e3;
3148
+ const TAKEOVER_POLL_MS = 100;
3149
+ function takeOverRunningInstance() {
3150
+ if (process.platform === "win32") return false;
3151
+ const pid = readLockOwnerPid();
3152
+ if (!pid) return false;
3153
+ if (!processIsDesktopPet(pid)) {
3154
+ logger.warn("single-instance lock held by an unrelated process, refusing to take over", { pid });
3155
+ return false;
3156
+ }
3157
+ logger.info("taking over running instance", { pid });
3158
+ try {
3159
+ process.kill(pid, "SIGTERM");
3160
+ } catch {
3161
+ return true;
3162
+ }
3163
+ if (waitForExit(pid, TAKEOVER_TIMEOUT_MS)) return true;
3164
+ logger.warn("instance did not exit after SIGTERM, forcing", { pid });
3165
+ try {
3166
+ process.kill(pid, "SIGKILL");
3167
+ } catch {
3168
+ return true;
3169
+ }
3170
+ return waitForExit(pid, 1e3);
3171
+ }
3172
+ function readLockOwnerPid() {
3173
+ let target;
3174
+ try {
3175
+ target = node_fs.readlinkSync(node_path.join(electron.app.getPath("userData"), "SingletonLock"));
3176
+ } catch {
3177
+ return void 0;
3178
+ }
3179
+ const hostPrefix = `${node_os.hostname()}-`;
3180
+ if (!target.startsWith(hostPrefix)) return void 0;
3181
+ const pid = Number(target.slice(hostPrefix.length));
3182
+ return Number.isSafeInteger(pid) && pid > 0 ? pid : void 0;
3183
+ }
3184
+ function processIsDesktopPet(pid) {
3185
+ try {
3186
+ const command = node_child_process.execFileSync("/bin/ps", ["-p", String(pid), "-o", "command="], { encoding: "utf8" });
3187
+ return /desktop[- ]pet/i.test(command);
3188
+ } catch {
3189
+ return false;
3190
+ }
3191
+ }
3192
+ function waitForExit(pid, timeoutMs) {
3193
+ const deadline = Date.now() + timeoutMs;
3194
+ while (Date.now() < deadline) {
3195
+ if (!processAlive(pid)) return true;
3196
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, TAKEOVER_POLL_MS);
3197
+ }
3198
+ return !processAlive(pid);
3199
+ }
3200
+ function processAlive(pid) {
3201
+ try {
3202
+ process.kill(pid, 0);
3203
+ return true;
3204
+ } catch {
3205
+ return false;
3206
+ }
3207
+ }
3147
3208
  const trayIcon1xPath = path.join(__dirname, "./chunks/tray-iconTemplate-BIK86F-0.png");
3148
3209
  const trayIcon2xPath = path.join(__dirname, "./chunks/tray-iconTemplate@2x-B19HpcaE.png");
3149
3210
  let tray;
@@ -3182,8 +3243,12 @@ if (profileDirectory) {
3182
3243
  electron.app.setPath("userData", node_path.resolve(profileDirectory));
3183
3244
  }
3184
3245
  registerAccountProtocol();
3185
- const singleInstance = electron.app.requestSingleInstanceLock();
3246
+ let singleInstance = electron.app.requestSingleInstanceLock();
3247
+ if (!singleInstance && takeOverRunningInstance()) {
3248
+ singleInstance = electron.app.requestSingleInstanceLock();
3249
+ }
3186
3250
  if (!singleInstance) {
3251
+ logger.warn("another instance is running, quitting");
3187
3252
  electron.app.quit();
3188
3253
  } else {
3189
3254
  electron.app.on("second-instance", (_event, argv) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "desktop-pet-app",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "description": "AI desktop pet with MCP support and a self-hosted relay server",
5
5
  "license": "MIT",
6
6
  "keywords": [